;*************************************************
;*						 *
;* Driver modules for Big Board Computer	 *
;*	running InfoSoft IOS 2.34		 *
;*						 *
;*	 Written by Trevor Marshall		 *
;*			May-June 1981		 *
;*						 *
;* (C) Copyright 1981 by Trevor Marshall	 *
;*						 *
;* 	Disk CACHE BUFFER version		 *
;*						 *
;*   Serial or Parallel Printer version		 *
;*	Honeywell or Clare keyboards		 *
;*						 *
;*************************************************
;
	NAME	MYDRV
;
	ENTRY	MYINIT
	ENTRY  	MYCLK
	ENTRY	MYCON
	ENTRY	MYTERM
	ENTRY	MYLIST
	ENTRY	MYPUN
	ENTRY	MYRDR
	ENTRY	MYBYE
	ENTRY	MYDUB
	ENTRY	LOGBUF
;
	EXT	CONPRY,WBOOT	;the IOS link to CONPROC
	EXT	CSDTE,UEJECT,DELAY
	EXT	SYABT,URREG,SPLLST
;
TRUE	EQU	-1
FALSE	EQU	0
;
SERIAL	EQU	FALSE	;This version for serial printer
PARALLEL EQU	NOT SERIAL ;or for parallel printer
IS5MHZ	EQU	TRUE	;5MHz or 2.5 MHz clock?
HONEYWELL EQU	TRUE	;Honeywell SD103 kybd csr codes
CLARE	EQU	NOT HONEYWELL	;CLARE C70/MGP keyboard
MULTI.USER EQU	TRUE	;Do we have 192K of RAM?
NO.CACHE EQU	FALSE	;Are we only using 64K RAM?
;
;
MYCON:	DW	MYCINIT,MYCSTAT,MYCIND,MYCRDY,MYCOUTD
MYTERM:	DW	MYCSET,MYCIN,MYCOUT
MYLIST:	DW	MYLINIT,MYLRDY,MYLOUT
MYRDR:	DW	MYRINIT,MYRSTAT,MYRIN
MYPUN:	DW	MYPINIT,MYPRDY,MYPOUT
MYCLK:  DW	SCLK,RDCLK
;
SCLK:
RDCLK:
		RET
MYBYE:	JP	0
;
;  I/OS CONSOLE DRIVER
;
MYCIND:
MYCIN:	CALL	DOCLOSE	;Close any DDBs to be written,then drop through to
CONIN:	JP	KBDIN	;These vectors are modified by the swstash routine
MYCINIT:
	XOR	A
	DEC	A	;**************************************?????????
	LD	(DELAY.FLG),A ;Indicate no delay on O/P chars
;
MYCRDY:
MYRSTAT:
MYPRDY:	LD	A,0FFH
MYPINIT:
MYPOUT:	RET
;
MYCOUT:
MYCOUTD:
CONOUT: JP	CRTOUT	;to reflect par or VDU system
;
MYCSTAT: LD	HL,(URREG) ;Allow DOS to continue spooling
	CALL	SPLLST
;Now check CLOSE timeout
	LD	HL,CLOSE.TICKS
	LD	E,(HL)		;We will check for 3000H calls to CONST
	INC	HL
	LD	D,(HL)
	LD	A,E
	OR	A
	JR	NZ,INC
	LD	A,D
	CP	30H
	JR	LT,INC
	PUSH	HL
	CALL	DOCLOSE	;If 3000H calls to CONST have elaped then CLOSE
	POP	HL
	LD	DE,0
INC:	INC	DE
	LD	(HL),D
	DEC	HL
	LD	(HL),E
;Then continue CONST call
CONST: JP	KBDST	; consoles
;
;
;  Set Special Console Command Including Cursor Addressing
;  Upon Entry:	for cursor addressing :
;		E contains cursor row in range 1-24
;		D contains cursor column in the range 1-80
;	    for special console command :
;		E = 0
;		D contains the special command number
;
;
MYCSET:	LD	A,E		; Check whether it's a special
	AND	A		;   or cursor-address command
	JR	Z,CSCOMMD	; Skip to do special command
	LD	A,1BH		; Escape character starts
	CALL	MYCOUT		;   cursor-address command
	LD	A,'='		; Second special character is "="
	CALL	MYCOUT		; Output it
	LD 	A,1FH		; Load A with offset to generate row
	ADD	E		; Add incoming row number to offset
	CALL	MYCOUT		; Output so-created character
	LD	A,1FH		; Load A with offset to generate column
	ADD	D		; Add incoming column number to offset
	JP	MYCOUT		; Output so-created character & return
;
;
;  Set special console entry command (part of CSET)
;  Upon entry:	D contains the special command number
;
CSCOMMD: LD	A,D		; Gte number of special command
	CP	SC.MAX		; Check for illegal special
	RET	NC		;   command and return if so 
	LD	HL,SC.TBL	; Point to table of special command values
;
	ADD	L		; Add offset in A to table address in HL
	LD	L,A		;   /
	JR	NC,CSC200	;  /
	INC	H		; /
CSC200:	LD	A,(HL)		; Get the command from the table
	AND	A		; Zero means command from table 
	RET 	Z		; Return if command not implemented
	JP	MYCOUT		; Output the special character
;
;  Special command table for ADM-3A terminal
;
SC.TBL:	DB	1AH		;  0 - Clear screen
	DB	1EH		;  1 - Home cursor
	DB	08H		;  2 - Back space
	DB	0CH		;  3 - Forward space
	DB	0BH		;  4 - Move cursor up
	DB	0AH		;  5 - Move cursor down
	DB	18H		;  6 - Clear to EOL
	DB	11H		;  7 - Clear to EOS
	DB	0		;not implemented
;
SC.MAX	EQU	$-SC.TBL	; Length of table

;

	IF	PARALLEL

MYLRDY:	IN	A,PIOBDAT	;Is printer busy?
	CPL
	AND	2	;if lo,busy
	RET	Z	;if so ret zero
	LD	A,0FFH
MYLINIT: RET

MYLOUT:	PUSH	AF
LP1:	CALL	MYLRDY	;Is printer busy?
	JR	Z,LP1
	POP	AF
	OUT	PIOADAT,A	;Write the data to port A
	IN	A,PIOBDAT	;Strobe port B
	RES	7,A
	OUT	PIOBDAT,A
	SET	7,A
	OUT	PIOBDAT,A
	RET

	ENDIF	;Parallel
;
	IF	SERIAL	;Using SIO channnel A
;
MYLRDY:	IN	A,6
	AND	4
	JR	NZ,CHECKCTS ;THe UART is ready, is the printer?
	XOR	A	;ready for
MYLINIT: RET		;not ready rtn
; Note that the SIO automatically has CTS status affects
;  the TBE bit, so this is not necessary
CHECKCTS ;IN	A,6
;	AND	20H	;ckech Clear To Send
;	LD	A,0
;	RET	Z	;A=0 if not ready
	LD	A,0FFH	;else A=0FFH
	RET

MYLOUT:	PUSH	AF
L11:	CALL	MYLRDY
	OR	A
	JR	Z,L11
	POP	AF
	OUT	4,A	;Output 8 bits
	RET

	ENDIF	;SERIAL
;
;
MYRIN:	LD	A,1AH	;<CTL-Z>
MYRINIT: RET
;
;
MYINIT:	DI		;Zero the monitor RAM area first
	LD	HL,MEM
	LD	DE,MEM+1
	LD	BC,MLNGTH-1
	LD	(HL),0
	LDIR
; Unless we switch to internal stack the bank switch fouls up, as the DOS hands
;   us a stack at 100H initially, which is bank switched out
	LD	(SPSAV2),SP
	LD	SP,STACK
;
;	INITIALIZE THE Z-80 FOR INTERRUPT MODE #2
	LD	HL,VECTAB
	LD	A,H
	LD	I,A 	;LOAD I REG WITH MSB OF VECTOR TABLE
	IM	2	; AND SELECT INTERRUPT MODE 2
;
	CALL	CLRSCN	;FILL THE CRT MEMORY WITH BLANKS
	IN	A,(BITDAT)
	RES	7,A		;Deselect CRT bank
	OUT	(BITDAT),A
;
; STORE ANY NON-ZERO VALUES FOR VARIABLES IN MEMORY
;
	LD	HL,INTAB ;POINT TO DEFAULT VARIABLE TABLE
INIT2:	LD	B,0
	LD	C,(HL)	;BC=DATA BLOCK BYTECOUNT
	INC	HL
	LD	E,(HL)	;DE=DESTINATION FOR DATA
	INC	HL
	LD	D,(HL)
	INC	HL
	LDIR		;COPY DATA @ HL TO VARIABLES @ DE
	BIT	7,(HL)
	JR	Z,INIT2	;LOOP AGAIN IF NOT AT END OF TABLE
;
; INITIALIZE THE PROGRAMMABLE I/O DEVICES
;
	INC	HL	;POINT TO I/O INIT DATA TABLE
INIT3:	LD	B,(HL)	;B=INIT LOOP BYTECOUNT
	INC	HL
	LD	C,(HL)	;C=DEVICE CONTROL PORT#
	INC	HL
	OTIR		;SEND DATA @ HL TO PORT @ C
	BIT	7,(HL)	;TEST FOR TABLE END MARKER
	JR	Z,INIT3	;LOOP AGAIN IF NOT AT END
;
; Because LINKA cannot relocate DBs we will have to program the vectors
;  for interrupts seperately
;
;DETERMINE IF CONSOLE I/O CONFIGURATION WILL BE FOR THE
;ON-BOARD CRT AND KEYBOARD OR AN EXTERNAL SERIAL TERMINAL
;
;First enable keyboard interrupts
	IN	A,(KBDDAT)	;Clear any rubbish
	LD	A,83H
	OUT	(KBDCTL),A ;ENABLE INTERRUPTS FROM KBD PIO
	EI
;
;	LD	C,SIOCPB
;	LD	A,2	;Select read register 2 for SIO test
;	OUT	(C),A
;	IN	A,(C)	;TEST SIO READ REGISTER 2 TO see
;	AND	0FH	;Allow for vectors on any 10H boundaries
;	CP	6H	 ; IF THE SIO IS INSTALLED
;	JR	NZ,PARAL ;SKIP CONFIGURATION TEST IF NO SIO
;	LD	B,10H ;B=RESET SIO EXTERNAL STATUS COMMAND  
;DECIDE:	OUT	(C),B	;TEST FOR ARRIVAL OF A SERIAL
;	IN	A,(C)	; INPUT CHARACTER START BIT
;	BIT	4,A
;	JR	NZ,BAUD	;EXIT LOOP IF START BIT DETECTED
;	CALL 	KBDST	;See if FIFO has char ready from
;	JR	Z,DECIDE ; parallel keyboard yet
;
;PARAL:	CALL	KBDIN	;clear char

SIGNON:
;
	IF	MULTI.USER
	LD	A,58H	;now select and home A:
	ELSE
	LD	A,48H	;now select and home A:
	ENDIF	;MULTI.USER
	OUT	(BITDAT),A
	CALL	HOME

;We need to create DDBs for both drives
	LD	HL,UNIT
	LD	(HL),0
	PUSH	HL
	CALL	MAKE.DDB
	POP	HL
	INC	(HL)	;Now drive B
	PUSH	HL
	CALL	MAKE.DDB
	POP	HL
	LD	(HL),0FFH
;
;now to the DOS
	LD	SP,(SPSAV2)	;Retrieve entry stack
	RET	;** Go execute IOS **


;
; AUTOMATIC BAUD RATE SETTING ROUTINE FOR SIO
;
;BAUD:	XOR	A
;BAUD1:	OUT	(C),B
;	IN	D,(C)	;READ SIO STATUS REGISTER
;	BIT	4,D	;TEST THE SYNC/HUNT BIT
;	JR	Z,BAUD1	;LOOP UNTIL IT CHANGES STATE
;BAUD2:	INC	A
;	OUT	(C),B	;RESET REGISTER #0 FLAGS AGAIN
;	IN	D,(C)	;AND LOOP TIMING THE SYNC/HUNT BIT
;	BIT	4,D
;	JR	NZ,BAUD2 ;REPEAT UNTIL BIT CHANGES AGAIN
;	LD	HL,RATES-1
;
;	IF	IS5MHZ
;	RRA		; /2 compensates for 5 MHz clock
;	ENDIF	;IS5MHZ
;
;BAUD3:	INC	HL	;INDEX INTO BAUD RATE TABLE
;	RLA		; USING COUNT DERIVED IN A
;	JR	NC,BAUD3
;	LD	A,(HL)	;GET BAUD RATE CONTROL BYTE FROM
;	OUT	(BAUDB),A ;TABLE AND OUTPUT TO COM-8116 TIMER
;	CALL	SIOIN	;DISARD FIRST SERIAL INPUT CHARACTER
;	LD	HL,SIOOUT
;	LD	DE,SIOIN	;Also SIO input
;	LD	BC,SIOST
;	CALL	SWSTSH ;And modify vectors for VDU
;	JR	SIGNON
;
;
; BAUD RATE CONSTANTS FOR COM 8116 BAUD RATE GENERATOR
;
;RATES:	DEFB	5H	;   300 BAUD
;	DEFB	6H	;   600 BAUD
;	DEFB	7H	;  1200 BAUD
;	DEFB	0AH	;  2400 BAUD
;	DEFB	0CH	;  4800 BAUD
;	DEFB	0EH	;  9600 BAUD
;	DEFB	0FH	; 19200 BAUD
;	DEFB	0FH	; 19200 BAUD
;
;
INTAB	EQU	$	;INITIALIZATION DATA TABLES
;
;INITIALIZE THE Z-80 'I' REGISTER INTERRUPT VECTOR TABLE
;
	DEFB	2
	DEFW	SYSVEC+2
	DEFW	KEYSRV ;PARALLEL KEYBOARD INTERRUPT VECTOR
;
	DEFB	2
	DEFW	CTCVEC+6
	DEFW	TIMER ;ONE SECOND TIMER INTERRUPT VECTOR

;Note that channel A vectors are not programmed (see Leventhal)
;	DEFB	4
;	DEFW	SIOVEC+4
;	DEFW	SIOINT	;SIO RECIEVE INTERRUPT VECTOR
;	DEFW	SIOERR	;SIO PARITY, OVERRUN & FRAMING ERROR
;
;
;	INITIALIZE DISK I/O DRIVER VARIABLES
;
	DEFB	6
	DEFW	UNIT
	DEFB	0	;FLAG drive A as selected
	DEFB	0,0	;CLEAR HEAD POSITION TABLE
	DEFB	0,0	;Both default single density
	DEFB	30	;SET MOTOR TURN-OFF TIMER
;
;	INITIALIZE THE CRT DISPLAY CURSOR
;
	DEFB	2
	DEFW	CHRSAV
	DEFB	' '
	DEFB	'_'+80H	;USE BLINKING UNDERSCORE
;
;	SET DEFAULT 'SOFTWARE' INTERRUPT VECTORS
;
	DEFB	6
	DEFW	TIKVEC
	DEFW	DSKTMR ;POINT 'TIKVEC' TO DISK MOTOR TIMER
	DEFW	STASH ;POINT 'PINVEC' TO FIFO INPUT ROUTINE
	DEFW	STASH ;POINT 'SINVEC' TO FIFO INPUT ROUTINE
;
;
	DEFB	-1	;END OF VARIABLE INIT TABLE
;
;
PIOADAT: EQU	8
PIOACTL: EQU	9
PIOBDAT: EQU	0AH
PIOBCTL: EQU	0BH
	DB	3,PIOACTL
	DB	3H	;Disable interrupts
	DB	0CFH	;Put in BIT mode
	DB	0	;All bits outputs
;
	DB	3,PIOBCTL
	DB	3H	;Disable interrupts
	DB	0CFH	;Put in BIT mode
	DB	6H	;bits 1,2 INPUTS
;
	DB	1,PIOBDAT
	DB	80H	;Bell off
;
BAUDA	EQU	00H ;CHANEL A BAUD RATE GENETATOR
SIO	EQU	04H ;DUAL SERIAL I/O
GENPIO	EQU	08H ;GENERAL PURPOSE PARALLEL I/O
BAUDB	EQU	0CH ;CHANEL B BAUD RATE GENERATOR
WD1771	EQU	10H ;WESTERN DIGITAL DISK CONTROLLER
SCROLL	EQU	14H ;CRT SCROLL MEMORY SCROLL REGISTER
CTC	EQU	18H ;QUAD COUNTER/TIMER CIRCUIT
SYSPIO	EQU	1CH ;SYSTEM PARALLEL I/O
;
;INITIALIZE SYSTEM PIO FOR USE AS BANK-SWITCH,
; DISK DRIVE SELECT AND PARALLEL KEYBOARD INPUT
;
BITDAT	EQU	SYSPIO+0
BITCTL	EQU	SYSPIO+1
KBDDAT	EQU	SYSPIO+2
KBDCTL	EQU	SYSPIO+3
;
;	DEFB	3,BITCTL
;	DEFB	0CFH	 ;PUT SYSTEM PIO IN BIT MODE
;	DEFB	0	 ;All bits outputs
;	DEFB	40H	 ;DISABLE INTERRUPTS
;
;	DEFB	1,BITDAT
;	DEFB	08H	 ;DE-SELECT ROMS, ENABLE DRIVE 0
;			; DD* line high
;			; SIDE 2 line low (=side 1)
;
	DEFB	2,KBDCTL
	DEFB	4FH	;PUT KEYBOARD PORT IN INPUT MODE
	DEFB	(SYSVEC+2)&0FFH ;LOAD KEYBOARD INTERRUPT VECTOR
;
;
;	INITIALIZE CHANELS 2 AND 3 OF THE CTC
;	TO GENERATE ONE SECOND INTERRUPTS FROM CTC3
;
CTC0	EQU	CTC+0		;CTC CHANEL 0 PORT#
CTC1	EQU	CTC+1		;CTC CHANEL 1
CTC2	EQU	CTC+2		;CTC CHANEL 2
CTC3	EQU	CTC+3		;CTC CHANEL 3
;
;	DEFB	1,CTC0
;	DEFB	CTCVEC&0FFH ;BASE INTERRUPT VECTOR FOR CTC
;
;	DEFB	2,CTC2
;	DEFB	00100111B ;PUT CTC2 IN TIMER MODE
;	DEFB	105	;CTC2 PERIOD=105*256*200 nsecs (5MHz)
;
;	DEFB	2,CTC3
;	DEFB	11000111B ;PUT CTC3 IN COUNTER MODE
;	DEFB	93    ;CTC3 PERIOD=999936/2 MICROSECONDS
;
;
;INITIALIZE SIO CHANEL B FOR ASYNCHRONOUS SERIAL
; INTERFACE TO TERMINAL
; SIO CHANNEL A is used for a serial printer
;
SIODPA	EQU	SIO+0	;SIO DATA PORT A
SIODPB	EQU	SIO+1	;SIO DATA PORT B
SIOCPA	EQU	SIO+2	;SIO CONTROL/STATUS PORT A
SIOCPB	EQU	SIO+3	;SIO CONTROL/STATUS PORT B
;
;	DEFB	1,BAUDB
;	DEFB	0101B ;SET COM 8116 FOR 300 BAUD DEFAULT
;
;	DEFB	10,SIOCPB
;	DEFB	4	  ;SELECT REGISTER #4
;	DEFB	01001100B ;16X CLOCK, 2 STOP BIT, NO PARITY
;	DEFB	1	  ;SELECT REGISTER #1
;	DEFB	00000100B ;STATUS AFFECTS VECTOR, NO INTERRUPTS
;	DEFB	3	  ;SELECT REGISTER #3
;	DEFB	11000001B ;8 BITS/RX CHARACTER
;	DEFB	5	  ;SELECT REGISTER #5
;	DEFB	11101010B ;7 BITS/TX CHARACTER, ASSERT DTR
;	DEFB	2	  ;SELECT REGISTER #2
;	DEFB	SIOVEC&0FFH ;LOAD INTERRUPT VECTOR BASE
;
;ALways initialize channel A to 1200 no ints, PAR: or SER:
	DB	1,BAUDA
	DB	0111B	;1200 Baud printer default
;
	DB	8,SIOCPA
	DB	1
	DB	0	;no interrupts
	DB	4	;register 4
	DB	4CH	;01001100B ;1.5 stop bits, no ints
	DB	3
	DB	0E1H	;11100001B ;8 bits/char, auto enables
	DB	5
	DB	0EAH	;11101010B ;8 bits/cahr
;
	DEFB	-1		;END-OF-TABLE
;
;	INIT DONE
;
;
;
;	-- SWITCH CONSOLE DEVICE --
;SWSTSH: ;Store the Serial/Parallel vectors
;SWIT2:	LD	(CONOUT+1),HL ;STORE NEW CONSOLE OUTPUT ADDRESS
;	LD	(CONIN+1),DE
;	LD	(CONST+1),BC
;	RET
;


; PMSG PRINTS THE STRING OF ASCII CHARACTERS
;  POINTED TO BY THE RELATIVE ADDRESS IN DE
;  UNTIL AN EOT IS ENCOUNTERED IN THE STRING.
;
EOT	EQU	04H
CR	EQU	0DH
LF	EQU	0AH
;
;Needed to output copyright msg	
PNEXT:	EX	(SP),HL
	CALL	PMSG
	EX	(SP),HL
	RET
;
PMSG:	LD	A,(HL)
	INC	HL
	CP	EOT
	RET	Z
	CALL	CONOUT
	JR	PMSG
;
;
;ASCHEX: SUB	'0'
;	RET	C
;	CP	10
;	CCF
;	RET	NC
;	SUB	7
;	CP	10
;	RET	C
;	CP	16
;	CCF
;	RET
;
;
PUT4HEX: LD	A,H
	CALL	PUT2HEX
	LD	A,L
PUT2HEX:
	PUSH	AF
	RRA	
	RRA	
	RRA	
	RRA	
	CALL	PUTNIB
	POP	AF
PUTNIB:	AND	00001111B
	ADD	A,90H
	DAA
	ADC	A,40H
	DAA
	JP	CONOUT
;
;
;********************************************************
;*							*
;*	INTERRUPT SERVICE ROUTINES FOR KEYBOARD		*
;*	INPUT AND REAL-TIME CLOCK FUNCTIONS		*
;*							*
;********************************************************
;
;
;
KBDST:	LD	A,(FIFCNT)	;GET INPUT FIFO BYTECOUNT
	OR	A		;TEST IF EQUAL ZERO
	RET	Z		;EXIT WITH A=0 IF QUEUE IS EMPTY
;To implement Type Ahead now check to see if DOS has one char queued yet
	PUSH	HL
	PUSH	DE
	LD	HL,(URREG)
	LD	DE,28	;URCHR offset in URREG
	ADD	HL,DE
	LD	A,(HL)
	OR	A
	JR	Z,RET.RDY ;If there is no URCHR then OK to send ready
;Here there is a URCHR, so check to see whether to overwrite
; will only pass thru CTL-S,CTL-C,CTL-E,CTL-P and <RETURN>
	CALL	REMOVE
	PUSH	AF
	CALL	REPLACE ;We only want to look at it, not ZAP it
	POP	AF
;Use <^S>,<^C> and <RETURN> sequence to abort batch
;	CP	'S'-40H
;	JR	EQ,RET.RDY
;	CP	'C'-40H
;	JR	EQ,RET.RDY
;	CP	'E'-40H
;	JR	EQ,RET.RDY
;	CP	'P'-40H
;	JR	EQ,RET.RDY
	CP	0DH
	JR	EQ,NR	;Don't pass CR
	CP	08H
	JR	EQ,NR	;Or BS
	CP	20H
	JR	LT,RET.RDY ;All other control chars to go thru
NR:	XOR	A	;else not ready
	JR	RET.NR
RET.RDY: LD	A,255
RET.NR:	POP	DE
	POP	HL
	OR	A	;Set Z flag
	RET			;ELSE SET A=255 TO INDICATE DATA READY
;
KBDIN:	LD	A,(FIFCNT)
	OR	A
	JR	NZ,KBDRDY	;LOOP UNTIL KEYBOARD INPUT READY
	PUSH	HL
	PUSH	DE
	PUSH	BC
	LD	HL,(URREG) ;But do it so as to keep spooler active
	CALL	SPLLST		;while wasting minimal time
	POP	BC
	POP	DE
	POP	HL
	JR	KBDIN
KBDRDY:	PUSH	HL
	CALL	REMOVE		;GET CHARACTER FROM INPUT QUEUE
	POP	HL

	IF	HONEYWELL
	BIT	7,A
;If B7 set it must be KYBD char code
	RET	Z
	ENDIF	;HONEYWELL

; We will process all keys for special codes if CLARE is in use
; Else catch the cursor codes for Honeywell keyboard
; 81=UP, 83=right, 84=left, 82=down, 88= home
; E7=CLRSCREEN is caught in STASH immediately it is pressed
;Similar proceedure for CLARE kybd
	PUSH	HL
	PUSH	BC
	LD	HL,TBL1	;Point at keycode table
	LD	BC,TBL1.LENGTH
	CPIR	;Until match or no match
	JR	NZ,RET1	;Abort if no match
	LD	BC,TBL1.LENGTH ;Point into next table
	ADD	HL,BC
	LD	A,(HL)
RET1:	POP	BC
	POP	HL
	RET

	IF	HONEYWELL
TBL1:	DB	81H
	DB	83H
	DB	84H
	DB	82H
	DB	0FFH	;a dummy entry for debugging
TBL1.LENGTH: EQU $-TBL1
	DB	0 ;1 byte spare as HL is incremented past match
TBL2:	DB	05H	;Up line
	DB	04H	;Right char
	DB	13H	;Left char
	DB	18H	;Down line
	DB	0	;our dummy entry again
;HOME is not processed, as not used by WORDSTAR

STASH:	LD	C,A
	CP	0E7H	;Honeywell Keyboard's CLEAR SCREEN key
	JR	NZ,NXT35
	CALL	CLRSCN	;Clear the screen
	IN	A,(BITDAT)
	RES	7,A
	OUT	(BITDAT),A
	RET
	ENDIF	;HONEYWELL


	IF	CLARE
TBL1:	DB	0BH	;Up line
	DB	89H	;right char
;	DB	08H	;left char	;handled without
;	DB	8AH	;down line	;translation
	DB	0FFH	;dummy
TBL1.LENGTH: EQU $-TBL1
	DB	0
TBL2:	DB	05H
	DB	04H
	DB	0
STASH:	AND	7FH	;delete parity
	LD	C,A
	ENDIF	;CLARE
;
NXT35:	
	IF	HONEYWELL
	CP	85H	;F1 key
	ENDIF	;HONEYWELL
	IF	CLARE
	CP	0AAH
	ENDIF	;CLARE
	JR	Z,NXT135 ;and toggle delay flag
;
;To ensure safe type ahead we must check to see if a ^S,^P,^C,^E are present
; We will in fact pass any control char other than <CR> or <BS>
	CP	0DH
	JR	Z,OK
	CP	08H
	JR	Z,OK
	CP	20H
	JR	GE,OK
;Else we want to drop the rest of the buffer as ctl char takes priority
	LD	A,1
	LD	(FIFCNT),A
	LD	(FIFIN),A
	LD	(FIFOUT),A
	JR	ST56
;
OK:	LD	HL,FIFCNT	;BUMP INPUT FIFO CHARACTER COUNT
	LD	A,(HL)
	INC	A
	CP	64	;**********************************-----------****
	RET	NC		;EXIT NOW IF FIFO IS FULL
	LD	(HL),A		; ELSE INCREMENT FIFO COUNT
ST56:	LD	HL,FIFIN	;POINT HL TO FIFO INPUT OFFSET
	CALL	INDEX
	LD	(HL),C		;STORE CHARACTER IN FIFO @ HL
	RET
;
NXT135:	LD	A,(DELAY.FLG)
	CPL		;toggle delay flag
	LD	(DELAY.FLG),A
	RET
;
REMOVE:	LD	HL,FIFCNT
	DEC	(HL)
	LD	HL,FIFOUT	;POINT HL TO FIFO OUTPUT OFFSET
INDEX:	LD	A,(HL)
	INC	A
	AND	3FH	;INCREMENT FIFO POINTER
	LD	(HL),A	; MODULO 64 AND REPLACE***********---------****
	LD	HL,BIGFIFO ;*******************************--------****
	ADD	L	;INDEX INTO FIFO BY OFFSET IN A
	LD	L,A
	LD	A,(HL)
	RET
;
REPLACE: PUSH	HL ;For CSPIM
	 LD	HL,FIFCNT
	INC	(HL)
	LD	HL,FIFOUT
INDEX2:	LD	A,(HL)
	DEC	A
	AND	3FH ;***************-------*************
	LD	(HL),A
	POP	HL
	RET
;
;
;	SOFTWARE DISK MOTOR TURN-OFF TIMER ROUTINE
;
DSKTMR:	LD	HL,MOTOR	;DECREMENT DISK TURN-OFF TIMER
	DEC	(HL)
	RET	NZ		;EXIT IF NOT TIMED OUT YET

;	CALL	DOCLOSE	;Not enough stack for this here

MOTORS.OFF: IN	A,(BITDAT)
	OR	04H		;DISABLE ALL DRIVE SELECTS AND
	OUT	(BITDAT),A	; TURN OFF THE SPINDLE MOTORS
	RET
;
;
;	-- INTERRUPT SERVICE ROUTINE FOR PARALLEL KEYBOARD --
;
KEYSRV:	LD	(SPSAVE),SP	;SAVE USER STACK POINTER AND
	LD	SP,TMPSTK	; SWITCH TO LOCAL STACK
	PUSH	HL
	PUSH	DE
	PUSH	BC
	PUSH	AF		;SAVE MACHINE STATE
	IN	A,(KBDDAT)	;READ KEYBOARD INPUT PORT
	CPL
	LD	HL,(PINVEC)	;GET KBD INTERRUPT ROUTINE VECTOR
	JR	DSPTCH	; AND JUMP TO DISPATCH POINT
;
;
;
;	-- INTERRUPT SERVICE ROUTINE FOR ONE SECOND TIMER --
;
TIMER:	LD	(SPSAVE),SP	;SAVE USER STACK POINTER AND
	LD	SP,TMPSTK	; SWITCH TO LOCAL STACK
	PUSH	HL
	PUSH	DE
	PUSH	BC
	PUSH	AF
	LD	HL,(TIKVEC)	;GET CLOCK INTERRUPT ROUTINE VECTOR
	JR	DSPTCH	; AND JUMP TO DISPATCH POINT
;
;
;
;	-- SERIAL INPUT INTERRUPT SERVICE ROUTINE FOR SIO --
;
;SIOINT: 
;
;	LD	(SPSAVE),SP	;SAVE USER STACK POINTER AND
;	LD	SP,TMPSTK	; SWITCH TO LOCAL STACK
;	PUSH	HL
;	PUSH	DE
;	PUSH	BC
;	PUSH	AF		;SAVE MACHINE STATE
;	IN	A,(SIODPB)	;READ SIO DATA INPUT PORT
;	AND	7FH
;	LD	HL,(SINVEC)	;GET SERIAL INPUT ROUTINE VECTOR
;
;
DSPTCH: CALL	CALLHL		;CALL SUBROUTINE ADDRESSED BY H
	POP	AF
	POP	BC
	POP	DE
	POP	HL
	LD	SP,(SPSAVE)
	EI			;RE-ENABLE INTERRUPTS AND RETURN
	RETI
;
CALLHL:	JP	(HL)
;
;
;
;	-- RX ERROR INTERRUPT SERVICE ROUTINE FOR SIO --
;
;	ARRIVE HERE IF RECIEVE INTERRUPT FROM FRAMING, OVERRUN
;	AND PARITY ERRORS. (PARITY CAN BE DISABLED)
;
;SIOERR: 
;
;	LD	(SPSAVE),SP	;SAVE USER STACK POINTER AND
;	LD	SP,TMPSTK	; SWITCH TO LOCAL STACK
;	PUSH	AF
;	CALL	SIOIN2		;CLEAR BAD CHARACTER FROM SIO
;	LD	A,'G'-64
;	CALL	SIOXMT		;OUTPUT A CTL-G AS A WARNING
;	POP	AF
;	LD	SP,(SPSAVE)
;	EI
;
;
;	RETI
;
;
;
;	POLLED MODE I/O ROUTINES FOR SIO CHANEL B
;
;SIOST:	IN	A,(SIOCPB)	;GET SIO STATUS REGISTER
;	AND	1
;	RET	Z		;ACC=0 IF NO DATA AVAILABLE
;	LD	A,255
;	RET
;
;
;SIOIN:	CALL	SIOST		;TEST CONSOLE STATUS
;	JR	Z,SIOIN	;LOOP UNTIL DATA IS RECIEVED
;SIOIN2: ;LD	A,30H		;RESET STATUS BITS IN SIO FOR
;	OUT	(SIOCPB),A	; PARITY/OVERRUN/FRAMING ERRORS,
;	IN	A,(SIODPB)	; THEN GET THE INPUT CHARACTER 
;	AND	7FH
;	RET
;
;
;SIOOUT:
;SIOXMT: PUSH	AF
;SIOX1:	IN	A,(SIOCPB)
;	AND	4	;TEST TBE STATUS BIT
;	JR	Z,SIOX1
;	POP	AF
;	OUT	(SIODPB),A	;OUTPUT DATA TO SIO
;	RET
;
;
;
;************************************************
;*						*
;*	MEMORY-MAPPED CRT OUTPUT DRIVER		*
;*						*
;************************************************
;
;
CRTMEM	EQU	3000H	;Start of 3K CRT memory space
CRTBAS	EQU	30H ;STARTING PAGE# OF 3K CRT SPACE
CRTTOP	EQU	3CH ;ENDING PAGE# OF CRT SPACE
;
;
CRTOUT:	
	PUSH	AF
	PUSH	HL
	PUSH	DE
	PUSH	BC
;	RES	7,A
	LD	C,A
;
;See if DELAY flag has been set
	LD	A,(DELAY.FLG)
	OR	A
	JR	Z,DI1
	LD	A,4	;4 msec delay to allow easier typing, etc
	CALL	DELAY	;IN IOS
DI1:	DI		;KEEP THE WOLVES AWAY FOR A WHILE
	LD	(SPSAVE),SP
	LD	SP,TMPSTK ;POINT SP TO TOP OF LOCAL STACK
	IN	A,(BITDAT)
	SET	7,A	    ;SELECT ROM/CRT MEMORY BANK
	OUT	(BITDAT),A
;
;FIRST REMOVE THE OLD CURSOR CHARACTER FROM THE SCREEN
;
	LD	HL,CHRSAV ;GET CHARACTER NOW OVERLAYED BY CURSOR
	LD	B,(HL)
	LD	HL,(CURSOR) ;LOAD HL WITH CURSOR POINTER
	LD	A,H
	AND	0FH	 ;A LITTLE INSURANCE THAT HL CAN'T
	OR	CRTBAS	 ; EVER POINT OUTSIDE THE CRT MEMORY
	LD	H,A
	LD	(HL),B	;REMOVE CURSOR BY RESTORING CHARACTER
;
;	PROCESS CHARACTER PASSED IN C
;
	CALL	OUTCH
;
;NOW STORE A NEW CURSOR CHARACTER AT THE CURSOR LOCATION
;
	LD	A,(HL)	;GET CHARACTER AT NEW CURSOR LOCATION
	LD	(CHRSAV),A ;SAVE FOR NEXT TIME 'CRTOUT' IS CALLED
	CP	' '	;TEST IF CHARACTER IS A SPACE
	SET	7,A	;THEN TURN ON BIT 7 TO ENABLE BLINK
	JR	NZ,CRT2	;JUMP IF CHARACTER IS NON-BLANK
	LD	A,(CSRCHR) ;ELSE GET CHARACTER USED FOR CURSOR
CRT2:	LD	(HL),A	;STORE CHARACTER IN A AS CURSOR MARK
	LD	(CURSOR),HL ;SAVE HL AS CURSOR POINTER

	LD	SP,(SPSAVE)
	IN	A,(BITDAT)
	RES	7,A	;SWITCH BACK THE LOWER 16K OF RAM
	OUT	(BITDAT),A
	EI		;INTERRUPTS ARE SAFE AGAIN
	POP	BC
	POP	DE
	POP	HL
	POP	AF
	RET
;
;
;
OUTCH:	LD	DE,LEADIN
	LD	A,(DE)	;GET LEAD-IN SEQUENCE STATE
	OR	A
	JP	NZ,MULTI ;JUMP IF IN A LEAD-IN SEQUENCE
	LD	A,C	; ELSE PROCESS CHARACTER IN C
	CP	' '
	JR	C,CONTRL ;JUMP IF A CONTROL CHARACTER
	BIT	7,A	;Is the MSBit set (Graphics char)
	JR	Z,DISPLA ;No, skip translation
	RES	7,A	;Graphics, select control char
DISPLA:	LD	(HL),C	; ELSE STORE DISPLAYABLE CHARACTER
	INC	HL	; AND ADVANCE POINTER TO NEXT COLUMN
	LD	A,L
	AND	7FH ;EXTRACT COLUMN# FROM HL
	CP	80
	RET	C	;EXIT IF NOT PAST COLUMN 79
	CALL	RETUR	; ELSE DO AUTOMATIC CARRIAGE RETURN
	CALL	LFEED	; AND LINEFEED
	RET
;
;
;
CONTRL:	PUSH	HL
	LD	HL,CTLTAB ;SEARCH FOR CONTROL CHARACTER
	LD	BC,CTLSIZ ;HANDLING SUBROUTINE IN TABLE
	CALL	SEARCH
	POP	HL
	RET	NZ	;EXIT IF NOT IMPLEMENTED
	PUSH	BC
	RET	;DO SNEAKY JUMP TO PRESERVE REGISTERS

SEARCH:	CPIR		;SEARCH TABLE @HL FOR MATCH WITH A
	RET	NZ	;EXIT NOW IF SEARCH FAILS
	ADD	HL,BC
	ADD	HL,BC	;ADD RESIDUE FROM CPIR BYTECOUNT
	ADD	HL,BC	; TO HL 3 TIMES TO GET POINTER
	LD	C,(HL)	; TO ADDRESS PART OF TABLE ENTRY
	INC	HL
	LD	B,(HL)
	RET		;EXIT WITH Z=1 TO INDICATE MATCH
;

CTLTAB:	DEFB	1FH
	DEFB	1EH
	DEFB	1BH
	DEFB	1AH
	DEFB	18H
	DEFB	11H
	DEFB	0DH
	DEFB	0CH
	DEFB	0BH
	DEFB	0AH
	DEFB	09H
	DEFB	08H
	DEFB	07H

CTLSIZ:	EQU	$-CTLTAB

	DEFW	BELL	;CTL-G IS THE BELL
	DEFW	BAKSPC	;CTL-H IS CURSOR LEFT
	DEFW	TAB	;CTL-I IS TAB
	DEFW	LFEED	;CTL-J IS CURSOR DOWN
	DEFW	UPCSR	;CTL-K IS CURSOR UP
	DEFW	FORSPC	;CTL-L IS CURSOR RIGHT
	DEFW	RETUR	;CTL-M IS CARRIAGE RETURN
	DEFW	CLREOS	;CTL-Q IS CLEAR TO END-OF-SCREEN
	DEFW	CLREOL	;CTL-X IS CLEAR TO END-OF-LINE
	DEFW	CLRSCN	;CTL-Z IS CLEAR SCREEN
	DEFW	ESCAPE	;CTL-[ IS ESCAPE
	DEFW	HOMEUP	;CTL-^ IS HOME UP
	DEFW	STUFF	;CTL-_ IS DISPLAY CONTROL CHARS

;CTLSIZ	EQU	$-CTLTAB
;
;
ESCAPE:	LD	A,1
	LD	(DE),A	;SET LEAD-IN SEQUENCE STATE
	RET	     ; FOR XY CURSOR POSITIONING MODE
;
;
STUFF:	LD	A,4
	LD	(DE),A	;SET LEAD-IN SEQUENCE STATE
	RET		; FOR CONTROL CHAR OUTPUT MODE
;
;
BAKSPC	LD	A,L	;CHECK FOR LEFT MARGIN
	AND	7FH
	RET	Z	;ABORT IF IN LEFTMOST COLUMN
	DEC	HL	;BACK UP CURSOR POINTER
	RET
;
;
FORSPC:	LD	A,L	;CHECK FOR RIGHTMOST COLUNM
	AND	7FH
	CP	79
	RET	NC	;DO NOTHING IF ALREADY THERE
	INC	HL
	RET	    ;ELSE ADVANCE THE CURSOR POINTER
;
;
TAB:	LD	DE,8	;TABS ARE EVERY 8 COLUMNS
	LD	A,L	;GET COLUMN COMPONENT OF
	AND	38H	 ; PREVIOUS TAB POSITION
	ADD	E
	CP	80	;EXIT IF NEXT TAB COLUMN WOULD
	RET	NC	; BE PAST THE RIGHT MARGIN
	LD	A,L
	AND	0F8H	 ;ELSE INCREMENT THE CURSOR
	LD	L,A	  ; POINTER FOR REAL
	ADD	HL,DE
	RET
;
; Bell is on BIGBOARD port bit 4
BELL:
	IF	NOT MULTI.USER	;Don't use BELL if BANK 3 available
	IN	A,(BITDAT)
	SET	4,A	  ;TOGGLE BIT 5 OF SYSTEM PIO TO
	OUT	(BITDAT),A ;TRIGGER BELL HARDWARE TO SOUND
	LD	A,150	;# millisecs
	CALL	DELAY	;in IOS
	IN	A,(BITDAT)	;faster than push/pop
	RES	4,A
	OUT	(BITDAT),A

	ELSE
	IN	A,(PIOBDAT)
	SET	6,A
	OUT	(PIOBDAT),A
	LD	A,800	;*****************************
	CALL	DELAY
	IN	A,(PIOBDAT)
	RES	6,A
	OUT	(PIOBDAT),A

	ENDIF	;NOT MULTI.USER
;
	RET
;
;
RETUR:	LD	A,L
	AND	80H
	LD	L,A	;MOVE CURSOR POINTER BACK
	RET		; TO START OF LINE
;
;
; Now clear the screen
CLRSCN:	IN	A,(BITDAT)
	SET	7,A
	OUT	(BITDAT),A	;Select CRT bank
	LD	HL,CRTMEM
	PUSH	HL
	LD	DE,CRTMEM+1
	LD	BC,3072
	LD	(HL),' '
	LDIR		;FILL CRT MEMORY WITH SPACES
	POP	HL	;POINT TO HOME CURSOR POSITION
	LD	A,23
	LD	(BASE),A ;MAKE BASE LINE# BE 23 AND
	OUT	(SCROLL),A ; STORE IN SCROLL REGISTER
	RET
;
;
CLREOL:	PUSH	HL	;SAVE CURSOR POINTER
	LD	A,L
	AND	7FH ;GET COLUMN# COMPONENT OF
	LD	C,A	; CURSOR POINTER INTO C
	LD	A,80	;CALCULATE HOW MANY CHARACTERS
	SUB	C	; REMAIN ON CURRENT LINE
	LD	B,A
	CALL	CLR	;CLEAR REST OF LINE @ HL
	POP	HL
	RET
;
;
CLREOS:	CALL	CLREOL	;CLEAR REMAINDER OF CURRENT ROW
	PUSH	HL
	LD	A,(BASE)
	LD	C,A	;COPY BASE SCREEN ROW# TO C
CLRS1:	LD	A,L
	RLA
	LD	A,H
	RLA		;GET ROW# COMPONENT OF HL INTO A
	AND	1FH
	CP	C  ;SEE IF HL IS AT BOTTOM ROW OF SCREEN
	JR	Z,CLRS2	; AND LEAVE CLEAR LOOP IF SO
	CALL	DNCSR	;ELSE POINT HL TO NEXT ROW DOWN
	CALL	CLRLIN	; AND FILL THAT LINE WITH SPACES
	JR	CLRS1

CLRS2:	POP	HL	;RESTORE ORIGINAL CURSOR POINTER
	RET
;
;
UPCSR:	LD	DE,-128	;SUBTRACT 1 FROM ROW# COMPONENT
	ADD	HL,DE	; OF CURSOR POINTER IN HL
	LD	A,H
	CP	CRTBAS	;CHECK FOR UNDERFLOW OF POINTER
	RET	NC
	LD	H,CRTTOP-1 ;WRAP CURSOR AROUND MODULO 3K
	RET
;
;
DNCSR:	LD	DE,128	;ADD 1 TO ROW# COMPONENT
	ADD	HL,DE	;OF CURSOR POINTER IN HL
	LD	A,H
	CP	CRTTOP	;CHECK FOR OVERFLOW OF POINTER
	RET	C
	LD	H,CRTBAS ;RESET POINTER MODULO 128*24
	RET
;
;
;
LFEED:	LD	A,L
	RLA
	LD	A,H
	RLA		;EXTRACT ROW# COMPONENT OF HL
	AND	1FH
	LD	C,A	;COPY ROW# INTO C FOR SCROLL TEST
	CALL	DNCSR	;MOVE CURSOR TO NEXT ROW DOWN
	LD	A,(BASE) ;TEST IF CURSOR WAS ON BOTTOM ROW
	CP	C	; OF SCREEN BEFORE MOVING DOWN
	RET	NZ	;EXIT IF NOT AT BOTTOM

	PUSH	HL	;ELSE PREP TO SCROLL SCREEN UP
	CALL	CLRLIN	;FILL NEW BOTTOM LINE WITH SPACES
	ADD	HL,HL
	LD	A,H	;GET ROW# COMPONENT OF HL INTO A
	AND	1FH
	LD	(BASE),A ;STORE NEW BASE LINE#
	OUT	(SCROLL),A ;NOW SCROLL UP NEW BLANK BOTTOM LINE
	POP	HL
	RET
;
;
CLRLIN:	LD	A,L
	AND	80H	 ;POINT HL TO FIRST COLUMN OF ROW
	LD	L,A
	LD	B,80
CLR:	LD	(HL),' ' ;STORE ASCII SPACES AT ADDRESS IN HL
	INC	HL	 ; AND INCREMENT HL
	DJNZ	CLR	 ;REPEAT NUMBER OF TIMES GIVEN BY B
	RET
;
;
HOMEUP:	LD	C,' ' ;FAKE-OUT CURSOR ADDRESSING ROUTINE
	JR	SETROW	; TO DO HOMEUP ALMOST FOR FREE
;
;
MULTI:	EX	DE,HL ;UNCONDITIONALLY RESET THE LEAD-IN
	LD	(HL),0 ; STATE TO ZERO BEFORE GOING ON
	EX	DE,HL
	CP	1
	JR	NZ,M2TST
SETXY:	LD	A,C	;GET SECOND CHAR OF SEQUENCE
	CP	'='
	RET	NZ	;ABORT SEQUENCE IF NOT '='
	LD	A,2
	LD	(DE),A	;MAKE LEADIN=2 NEXT TIME
	RET

M2TST:	CP	2
	JR	NZ,M3TST
	LD	A,3
	LD	(DE),A	;MAKE LEADIN=3 NEXT TIME
SETROW:	LD	A,(BASE) ;ARRIVE HERE ON THIRD CHARACTER
	ADD	C	 ; OF ESC,'=',ROW,COL SEQUENCE
	SUB	' '-1
SETR2:	SUB	24
	JR	NC,SETR2 ;MAKE SURE ROW# IS BETWEEN 0 AND 23
	ADD	24
	OR	60H	 ;MERGE IN MSB'S OF CRT MEMORY
	LD	H,A
	LD	L,0
	SRL	H
	RR	L
	RET

M3TST:	CP	3
	JR	NZ,M4TST
SETCOL:	LD	A,C		;ARRIVE HERE ON FOURTH CHARACTER
	SUB	' '		; OF ESC,'=',ROW,COL SEQUENCE
SETC2:	SUB	80
	JR	NC,SETC2	;MAKE SURE COL# IS BETWEEN 0 AND 79
	ADD	80
	OR	L		;MERGE IN COL# WITH L
	LD	L,A
	RET

M4TST:	CALL	DISPLA		;DISPLAY THE CONTROL CHARACTER
	RET			; PASSED IN C
;
;  \\\\\\\\\\\\\\\\                  ////////////////
;   >>>>>>>>>>>>>>>>    F A S T     <<<<<<<<<<<<<<<<
;  ////////////////   M O D U L E    \\\\\\\\\\\\\\\\
;
; TITLE		CP/M SPEED-UP BY DISK I/O BUFFERING
; AUTHOR	Robert A. Van Valzah    12/25/78
; Rewritten for IOS on Big Board by Trevor Marshall
;
SECLEN	EQU	80H		;Default LENGTH OF A SECTOR IN BYTES
DIRTRK	EQU	2		;Default TRACK WHICH HOLDS DIRECTORY
;
SENTTOK EQU	1		;TOKEN SHOWING DRIVE & TRACK INFO SENT
UPDTTOK EQU	1		;TOKEN SHOWING SECTOR GETS WRITTEN
ONLNTOK EQU	3		;TOKEN SHOWING THAT A DRIVE IS 'ON LINE'
;
;
	ENTRY	CDDB	;For debugging
CDDB:	EQU	$
CREATE.DDB:
;	CALL	BANK2
	IN	A,(BITDAT)	
	RES	4,A
	OUT	(BITDAT),A
	LD	(SPSAV3),SP
	LD	SP,0BF00H

	LD	(HL),B	;Store track#
;
;	CALL	PNEXT
;	db	'CREATING DDB size ',EOT
;	LD	A,(POKE7+1)
;	CALL	PUT2HEX
;
	INC	HL
	LD	A,(UNIT) ;Get currently logged drive
	LD	(HL),A	;put drive #
	LD	B,1	;B holds the sector#
LP102:	;Number the sectors sequentially
	INC	HL
	LD	(HL),B	;now sector #
	XOR	A	;update token=0
	INC	HL
	LD	(HL),A
	LD	DE,81H	;data length + BIOS return code
	ADD	HL,DE	;to point at next sector info
	INC	B
	LD	A,B
POKE7:	CP	53	;Maxsectors+1
	JR	NZ,LP102
	INC	HL
	LD	(HL),0	;End of DDB marker
;	JP	RETURN
	IN	A,(BITDAT)
	SET	4,A
	OUT	(BITDAT),A
	LD	A,E
	LD	SP,(SPSAV3)
	RET
;
; FAST SET DISK DRIVE, EXTERNALLY REFERENCED ROUTINE
FSETDSK:
;
;	CALL	PNEXT
;	DB	'FsetDSK',EOT
;
;	CALL	BANK2	;For RDDDB below
	IN	A,(BITDAT)	;Exit, after reselecting main memory
	RES	4,A
	OUT	(BITDAT),A
	LD	(SPSAV3),SP
	LD	SP,0BF00H	;in baNk2
;
	LD	E,C		;SAVE DRIVE FOR ON LINE CODE
	LD	A,C		;GET REQUESTED DRIVE
	LD	(REQDSK),A	;SAVE FOR PASS THRU
	ADD	A		;DOUBLE TO FORM WORD INDEX
	LD	C,A		;FORM INDEX IN REG BC
	LD	B,0
	LD	HL,DIRBUF	;GET NEW DIR DDB POINTER
	CALL	INDXIND
	LD	(DIRADR),HL	;AND SAVE IT
	LD	HL,RDBUF	;GET NEW READ TRACK DDB POINTER
	CALL	INDXIND
	LD	(RDADR),HL	;AND SAVE IT
	LD	HL,WRBUF	;GET NEW WRITE TRACK DDB POINTER
	CALL	INDXIND
	LD	(WRADR),HL
	LD	C,E		;REG BC IS NOW BYTE INDEX
	LD	HL,ONLINE	;BASE OF 'ON LINE' VECTOR
	ADD	HL,BC		;ADD INDEX TO BASE
	LD	A,(HL)		;IS THIS DRIVE ON LINE?
	OR	A
	LD	A,0	;no errors on return please
	JP	NZ,RETURN	;YES - DONE WITH SECECTION
	LD	(HL),ONLNTOK	;NO - SET ON LINE FLAG AND . .
	LD	HL,(DIRADR)	;AND READ THE DIRECTORY IF IT IS BUFFERED
	CALL	RDDDB
	JP	RETURN
;
; FAST READ SECTOR
; EXTERNALLY REFERENCED ROUTINE.
;
FREAD:	
;	CALL	BANK2	;Switch in CACHE memory,already done in MYIO
	LD	HL,(RDADR)	;GET ADDRESS OF READ TRACK DDB
	CALL	GETSEC		;TRY TO FIND SECTOR IN READ DDB
	JP	C,RETURN	;Found it, switch back to BANK 0 & return
	LD	HL,(DIRADR)	;TRY TO FIND SECTOR IN DIRECTORY DDB
	CALL	GETSEC
	JP	C,RETURN	;Found it, switch back to BANK 0 & return
	LD	HL,(WRADR)	;TRY TO FIND SECTOR IN WRITE DDB
	CALL	GETWSEC
	JP	C,RETURN	;Found it, switch back to BANK 0 & return
	LD	HL,(RDADR)	;SEE IF THERE IS A READ TRACK BUFFER
	LD	A,H
	OR	L
	JP	Z,RDPASS	;NO - THEN JUST PASS THE READ THRU
	LD	A,(REQTRK)	;YES - SEE IF READING FROM NON-DIRECTORY

;POKE2:	CP	DIRTRK		;SECTORS OF DIRECTORY TRACK
;	JP	Z,RDPASS	;YES - JUST LET THAT PASS THRU

	OR	A	;See if we are trying for trk 0 *** Unbuffered please
	JP	Z,RDPASS	;Dont buffer trk#0
;
;Note: I have destroyed E here
	LD	E,A	;Will save trk# in E to miss flags problems
WRRET1:
	PUSH	DE
	PUSH	HL
	CALL	WRDDB
	POP	HL
	POP	DE
	JR	NZ,WRERR1	;If WRDDB returned an error, process it
ERRRE1	LD	(HL),E		;Fill new track into read DDB

;	LD	A,(WRTERRF)
;	OR	A
;	RET	NZ	;error

RDRET1:	PUSH	HL		;SAVE READ DDB ADDRESS
	CALL	RDDDB		;AND FILL NEW DDB WITH DATA
	POP	HL		;GET READ DDB ADDRESS BACK
	JR	NZ,RDERR1
ERRRE2	CALL	GETSEC		;TRANSFER SECTOR FROM DDB TO CALLER (was JP)
	JP	RETURN	; switch back to BANK 0 & return
;
WRERR1:	CALL	REPORT
	JR	C,ERRRE1 ;Ignore if carry set
	JR	NZ,WRRET1
	LD	HL,0	;Fake an abort return
	PUSH	HL
	JP	DOCLOSE	;Otherwise warn of a CLOSE error (and retry to WRDDB)
;
RDERR1: CALL	REPORT
	JR	C,ERRRE2 ;Ignore if carry set
	JR	NZ,RDRET1
	JP	EXIT		;Do a CTL-C exit here
;
REPORT:	LD	C,A	;Enter if error status is in A
REPORT2: ;Else here if it is in C
	CALL	PNEXT
	DB	CR,LF,'**** Disk error, status ',EOT
	LD	A,C	;status into A
	CALL	PUT2HEX
REPORT3: CALL	PNEXT
	DB	',enter <^R>, <^I> or <^A> only.',7,EOT
;	CALL	CONIN
	CALL	CONIN
	CP	13H	;>= ^S
	JR	GE,REPORT3
	ADD	40H
	PUSH	AF
	CALL	CONOUT	;echo the char
;now terminate the line
	CALL	PNEXT
	DB	CR,LF,EOT
	POP	AF
SKP34:
	CP	'I'
	SCF		;Set carry in case ignore is true
	RET	Z	;with carry test before zero test
	CP	'A'	;will default to retry
	SCF
	CCF		;clear carry
	RET
;
; FAST SECTOR WRITE, EXTERNALLY REFERENCED ROUTINE.
;
FWRITE:	

;	CALL	BANK2	;Switch in CACHE memory,already done in MYIO

;Now check drive's write protect status
	CALL	TURNON
	AND	40H
	JR	Z,NXT672
	CALL	PNEXT
	DB	'-> Drive is write protected',EOT
	CALL	REPORT3
	JR	C,NXT671 ;ignore
	JR	NZ,NXT672 ;retry
NXT671:	CALL	PNEXT
	DB	'Cant ignore',EOT
	JP	EXIT	;abort
;
NXT672:	LD	HL,(WRADR)	;TRY TO PUT SECTOR INTO WRITE BUFFER
	CALL	PUTSEC
	JP	C,RETURN	;It went, switch back to BANK 0 & return
	LD	HL,(DIRADR)	;TRY TO PUT IT INTO DIRECTORY BUFFER
	CALL	PUTSEC
	JP	C,RETURN	;It went, switch back to BANK 0 & return
	LD	HL,(RDADR)	;TRY TO PUT INTO READ BUFFER
	CALL	PUTSEC
	JP	C,RETURN	;It went, switch back to BANK 0 & return
	LD	HL,(WRADR)	;IS THERE A WRITE TRACK BUFFER?
	LD	A,H
	OR	L
	JP	Z,WRPASS	;NO - JUST PASS THIS WRITE THRU
	LD	A,(REQTRK)	;YES - SEE IF THIS IS A NON-DIRECTORY
;POKE3:	CP	DIRTRK		;SECTOR ON THE DIRECTORY TRACK?
;	JP	Z,WRPASS	;YES - JUST PASS THAT THRU
	OR	A	;Don't buffer trk#0
	JP	Z,WRPASS
WRRET2:			;No, Dump existing write buffer to disk

;	LD	C,A
;	CALL	PNEXT
;	DB	'Track ',EOT
;	LD	A,C
;	CALL	PUT2HEX
;	CALL	PNEXT
;	DB	', WRDDB= ',EOT
;	LD	A,H
;	CALL	PUT2HEX
;	LD	A,L
;	CALL	PUT2HEX	

	PUSH	HL
	CALL	WRDDB
	POP	HL

;	PUSH	AF
;	CALL	PNEXT
;	DB	'Returned from WRDDB with error code ',EOT
;	POP	AF
;	PUSH	AF
;	CALL	PUT2HEX
;	POP	AF

	JR	NZ,WRERR2
ERRRE3
	LD	A,(REQTRK)
	LD	(HL),A		;AND BUFFER UP REQUESTED TRACK

;	LD	A,(WRTERRF)
;	OR	A
;	RET	NZ
	CALL	PUTSEC		;MOVE SECTOR INTO WRITE BUFFER
	JP	RETURN	
;
WRERR2:	CALL	REPORT
	JR	C,ERRRE3	
	JR	NZ,WRRET2
	LD	HL,0
	PUSH	HL
;	JR	DOCLOSE	;warn of a close error
;
; CLOSE ALL OPEN DDB'S.  WRITE ANY DATA LEFT IN THEM OUT TO
; DISK
; EXTERNALLY REFERENCED ROUTINE.
;
	GLOBAL	CLOS1
CLOS1:
CLOSE:
DOCLOSE:			;see above
	XOR	A
	LD	(CLOSE.TICKS),A	;Zero the elapsed ticks since last close
	LD	(CLOSE.TICKS+1),A
CLOSE1:
;	CALL	PNEXT
;	DB	' Closing..TOS=',EOT
;	EX	(SP),HL
;	LD	C,L
;	LD	B,H
;	EX	(SP),HL
;	LD	HL,0
;	ADD	HL,BC
;	CALL	PUT4HEX
;	CALL	PNEXT
;	DB	' Stack ptr=',EOT
;	LD	HL,0
;	ADD	HL,SP
;	CALL	PUT4HEX
;
;	CALL	BANK2	;Select the CACHE memory
	IN	A,(BITDAT)
	RES	4,A
	OUT	(BITDAT),A
	LD	(SPSAV3),SP
	LD	SP,0BF00H	;in baNk2

	LD	C,2*3		;DDB COUNT
	LD	HL,RDBUF	;START OF DDB ADDRESS TABLES

CLOS:
;	PUSH	HL
;	LD	HL,(SPSAV3)
;	CALL	PNEXT
;	DB	' Start of close SPSAV3=',EOT
;	CALL	PUT4HEX
;	POP	HL
;
	LD	E,(HL)		;GET DDB ADDRESS TO REG DE
	INC	HL
	LD	D,(HL)
	INC	HL		;Pointing at next DDB
	PUSH	HL		;SAVE TABLE ADDRESS
	PUSH	BC
	EX	DE,HL		;DDB ADDRESS TO REG HL
;
;	CALL	PNEXT
;	DB	'Calling WRDDB..',EOT
;See also JR CLOSe1 below
;
	CALL	WRDDB		;CLOSE ONE DDB

;	PUSH	HL
;	LD	HL,(SPSAV3)
;	CALL	PNEXT
;	DB	' After WRDDB SPSAV3=',EOT
;	CALL	PUT4HEX
;	POP	HL

;	PUSH	AF
;	CALL	PNEXT
;	DB	'Done WRDDB..',EOT
;	POP	AF

	LD	A,(WRTERRF)	;ANY WRITE ERRORS IN CLOSING?
	OR	A
	JR	Z,CLOSOK	;NO - KEEP CLOSING
	CALL	PNEXT
 DB CR,LF,'-> CLOSE ERROR,it may be FATAL to abort or ignore',EOT
	CALL	REPORT3 ;Default to retry
	JR	C,CLOSOK ;on ignore
	JP	Z,EXIT	;on abort
	POP	BC
	POP	HL	;Balance stack
	JP	CLOSE1	;See also JP NZ below
CLOSOK:	

;	CALL	PNEXT
;	DB	'Close OK..',EOT

	POP	BC		;GET COUNT
	POP	HL		;GET TABLE ADDRESS
	DEC	C		;DONE ALL DDBS YET?
	JP	NZ,CLOS		;NO - KEEP CLOSING

;	CALL	PNEXT
;	DB	'Done close..',EOT

	XOR	A
	JP	RETURN		;YES - ALL DONE
;
; INDEXED INDIRECT ADDRESSING.  RETURNS THE WORD AT THE
; ADDRESS BASE + INDEX
; INTERNALLY REFERENCED ROUTINE.
; Entry at INDEXA gets the table address for a buffer
;
INDEXA:	LD	A,(UNIT)
	RLCA			;A = A * 2
	LD	B,0
	LD	C,A		;and drop through to get table entry
INDXIND:
	ADD	HL,BC		;ADD IN INDEX
	LD	A,(HL)		;GET LOW ORDER OF RESULT
	INC	HL
	LD	H,(HL)		;GET HIGH ORDER OF RESULT
	LD	L,A
	RET	
;
; GET A SECTOR FROM A WRITE DDB INTO REQDMA.  MATCHES ONLY
; IF UPDATE FLAG IS SET.  RETURNS FLAG IF FOUND, INTERNALLY REFERENCED ROUTINE.
;
GETWSEC:
	CALL	SRCHDDB		;FIND A SECTOR AND TRACK MATCH?
	RET	NC		;NO - RETURN WITHOUT FLAG
	INC	HL		;YES - SEE IF SECTOR HAS BEEN
	LD	A,(HL)		;WRITTEN (UPDATE FLAG=1)?
	OR	A
	JR	NZ,GS1		;YES - TRANSFER FROM WRITE DDB
	CALL	RDPASS		;NO - BYPASS BUFFERING
	SCF			;SET FOUND IT FLAG
	RET	
;
; GET A SECTOR FROM A DDB INTO REQDMA.  FLAG SET IF FOUND.
; INTERNALLY REFERENCED ROUTINE.
;
GETSEC:	
	CALL	SRCHDDB		;DO WE HAVE THE REQUESTED SECTOR?
	RET	NC		;NO - RETURN WITHOUT FLAG
	INC	HL		;YES - POINT TO SECTOR DATA
GS1:	;ENTRY FROM GETWSEC (ABOVE)
	INC	HL
	EX	DE,HL		;DATA ADDRESS TO REG DE
	LD	HL,(REQDMA)	;DESTINATION TO REG HL
;	CALL	XFER		;PERFORM THE TRANSFER
; MOVE A SECTOR FROM ONE PLACE IN MEMORY TO ANOTHER.
	LD	B,SECLEN
XF11:	
;	CALL	BANK2
	IN	A,(BITDAT)
	RES	4,A
	OUT	(BITDAT),A
	LD	A,(DE)
	LD	C,A		;Save it while
;	CALL	BANK0
	IN	A,(BITDAT)
	SET	4,A
	OUT	(BITDAT),A
	LD	(HL),C	;Could do an error check here
	INC	HL
	INC	DE
	DJNZ	XF11
;	CALL	BANK2	;So good/bad flag is correct
	IN	A,(BITDAT)
	RES	4,A
	OUT	(BITDAT),A
	LD	A,(DE)		;GET GOOD/BAD FLAG
	OR	A	;set the Z flag
	SCF			;SET 'FOUND IT' FLAG
	RET	
;
; SEARCH A DDB FOR THE REQUESTED TRACK AND SECTOR
; INTERNALLY REFERENCED ROUTINE.
;
SRCHDDB:
	LD	A,H		;SEE IF DDB IS PRESENT AT ALL
	OR	L
	RET	Z		;NO - WE DON'T HAVE IT
	LD	A,(REQTRK)	;YES - SEE IF REQTRK MATCHES DDB TRACK
	XOR	(HL)
	RET	NZ		;NO MATCH - WE DON'T HAVE IT
	INC	HL		;TRACKS MATCH - CHECK SECTORS
	INC	HL
	LD	A,(REQSEC)
	LD	C,A
PATCH2:	LD	DE,SECLEN+3	;BYTES BETWEEN SECTOR NUMBERS
SRCH1:	
	LD	A,(HL)		;GET A SECTOR NUMBER FROM DDB
	OR	A		;END OF DDB?
	RET	Z		;YES - RETURN WITHOUT FLAG
	CP	C		;NO - SEE IF REQSEC MATCHES DDB SECTOR
	SCF			;PREPARE TO RETURN FLAG IF SO
	RET	Z		;THEY MATCH - RETURN FLAG
	ADD	HL,DE		;NO MATCH - ON TO NEXT SECTOR
	JR	SRCH1
;
RDPASS:	
;	CALL	BANK0	;For unbufferred (trk 0)
	IN	A,(BITDAT)
	SET	4,A
	OUT	(BITDAT),A
	LD	SP,(SPSAV3)

	CALL	PASSTHRU
	JR	NZ,RDERR3
ERRRE5:	JP	READ
;
RDERR3:	CALL	REPORT
	JR	C,ERRRE5
	JR	NZ,RDPASS
	JP	0	;Abort system
;
; MOVE A SECTOR FROM MEMORY INTO A DDB
; INTERNALLY REFERENCED ROUTINE.
;
PUTSEC:	
	CALL	SRCHDDB		;DOES IT GO IN THIS DDB?
	RET	NC		;NO - RETURN WITHOUT FLAG
	INC	HL		;POINT TO DATA FIELD OF DDB
	LD	(HL),UPDTTOK	;SET UPDATE FLAG
	INC	HL
	EX	DE,HL		;SAVE DDB ADDRESS IN REG DE
	LD	HL,(REQDMA)	;GET SOURCE ADDRESS INTO REG HL
	EX	DE,HL		;NOW REG HL=DDB ADDRESS (DEST) AND . .
;	CALL	XFER		;REG DE=MEMORY (SOURCE)
; MOVE A SECTOR FROM ONE PLACE IN MEMORY TO ANOTHER.
	LD	B,SECLEN
XF12:
;	CALL	BANK0
	IN	A,(BITDAT)
	SET	4,A
	OUT	(BITDAT),A
	LD	A,(DE)
	LD	C,A		;Save it while
;	CALL	BANK2
	IN	A,(BITDAT)
	RES	4,A
	OUT	(BITDAT),A
	LD	(HL),C	;Could do an error check here
	INC	HL
	INC	DE
	DJNZ	XF12
;	CALL	BANK2	;So good/bad flag is correct
	XOR	A		;RETURN WITHOUT WRITE ERROR
	LD	(HL),A		;SET GOOD/BAD FLAG TO GOOD
	SCF			;SET SECTOR PUT FLAG
	RET	
;
WRPASS:	
;	CALL	BANK0
	IN	A,(BITDAT)
	SET	4,A
	OUT	(BITDAT),A
	LD	SP,(SPSAV3)
	CALL	PASSTHRU
	JR	NZ,WRERR3
ERRRE6	JP	WRITE
;
WRERR3: CALL	REPORT
;	JR	C,ERRRE6
	JR	NZ,WRPASS
	LD	HL,0
	PUSH	HL
	JP	DOCLOSE	;otherwise warn
; PASS ALL DISK I/O PARAMETERS THRU TO BIOS
; INTERNALLY REFERENCED ROUTINE
;
PASSTHRU:
	LD	A,(REQDSK)	;PASS REQUESTED DISK THRU
	LD	C,A
	CALL	SELECT
	RET	NZ
	LD	A,(REQTRK)	;PASS REQUESTED TRACK THRU
	LD	C,A	;prepare for SEEK
;Check if it is an illegal sector on TRK #0
	OR	A
	JR	NZ,NOT.TRK.0
	LD	A,(REQSEC)	;Is the sector illegal?
	CP	1BH	; >= 1BH?
	JR	C,NOT.TRK.0 ;C means .LT. 1BH
	XOR	A	;return no errors
	POP	HL	;tricky stack op, return to IOS, not to RDPASS
	RET		;return if .GE. 1BH on trk 0
NOT.TRK.0:
	CALL	SEEK
	RET	NZ
	LD	A,(REQSEC)	;PASS REQUESTED SECTOR
	LD	(SECTOR),A
	LD	HL,(REQDMA)	;PASS REQUESTED DMA ADDRESS
;	XOR	A	;Z flag
	RET	;Leave our DMA addr in HL
;
; READ A DDB FROM DISK.  ALL SECTORS ARE FILED IN, AND THEIR
; UPDATE FLAGS ARE RESET.
; INTERNALLY REFERENCED ROUTINE.
;
RDDDB:	
	LD	A,H		;DO NOTHING FI NO DDB
	OR	L
	RET	Z
	PUSH	HL		;SAVE DDB ADDRESS
	CALL	SENDDT		;SEND DRIVE AND TRACK INFO TO BIOS
	POP	HL		;GET DDB ADDRESS
	RET	NZ
	INC	HL		;POINT TO FIRST SECTOR BYTE
	INC	HL
RDDSEC:	
	LD	A,(HL)		;GET SECTOR NUMBER
	OR	A		;END OF DDB?
	RET	Z		;YES - ALL DONE
	LD	(SECTOR),A
	INC	HL		;POINT TO UPDATE FLAG
	LD	(HL),0		;RESET UPDATE FLAG
	INC	HL		;POINT TO DATA FIELD
	PUSH	HL		;SAVE DATA POINTER
;My read requires DMA addr in HL
	CALL	READ		;PERFORM THE READ
	POP	HL		;GET DATA ADDRESS BACK
	RET	NZ
PATCH3:	LD	BC,SECLEN	;BYTES TO GOOD/BAD FLAG
	ADD	HL,BC
	LD	(HL),A		;STORE GOOD/BAD FLAG FROM BIOS IN DDB
	INC	HL
	JR	RDDSEC		;ON TO NEXT SECTOR
;
; WRITE A DDB BACK TO DISK.  ONLY SECTORS WITH THE UPDATE FLAG
; SET GET WRITTEN.
; THE DRIVE AND TRACK INFORMATION FROM THE DDB IS SAVED BY
; THIS ROUTINE AND PASSED TO THE BIOS BY RCLDT ONLY IF
; A SECTOR IS TO BE WRITTEN.  THIS IS DONE TO BYPASS BIOS
; DELAYS WHEN SWITCHING DRIVES AND TO PREVENT NEEDLESS
; SEEKING TO TRACKS WHICH MIGHT NOT RECIEVE ANY DATA.
; INTERNALLY REFERENCED ROUTINE.
;
WRDDB:
	XOR	A		;CLEAR WRITE ERROR FLAG
	LD	(WRTERRF),A
	LD	A,H		;SEE IF A DDB IS PRESENT
	OR	L
	RET	Z		;NO - ALL DONE WRITING
	LD	A,(HL)		;GET TRACK NUMBER
	INC	A		;TRACK = 0FFH (MEANING EMPTY)?
	RET	Z		;YES - NO WRITING TO DO
	LD	(TRKPTR),HL	;NO - SAVE DISK & TRACK POINTER FOR
	XOR	A		;RECALL LATER, AND RESET FLAG 
	LD	(SENTFLG),A	;SHOWING THAT THEY HAVEN'T BEEN SENT
	INC	HL		;POINT TO FIRST SECTOR NUMBER
	INC	HL
TSTUPDT:
	LD	A,(HL)		;GET SECTOR TO REG A
	OR	A		;END OF DDB?
	RET	Z		;YES - ALL DONE
	LD	C,A		;SAVE SECTOR IN REG C
	INC	HL		;POINT TO UPDATE FLAG
	LD	A,(HL)		;GET UPDATE FLAG
	OR	A		;DOES THIS SECTOR NEED UPDATING ON DISK?

;	PUSH	AF
;	CALL	PNEXT
;	DB	',',EOT
;	POP	AF
;	PUSH	AF
;	CALL	PUT2HEX
;	POP	AF

	CALL	NZ,WRSEC	;YES - WRITE IT BACK
;	LD	A,(WRTERRF)	;Errors handled in WRSEC
;	OR	A
;	RET	NZ
PATCH4:	LD	DE,SECLEN+2	;BYTES TO NEXT SECTOR NUMBER
	ADD	HL,DE
	JR	TSTUPDT		;CHECK THE NEXT UPDATE FLAG
;
; WRITE A SECTOR FROM A DDB BACK OUT TO DISK.
; INTERNALLY REFERENCED ROUTINE.
;
WRSEC:	
;Defer reset until errors notified
;	LD	(HL),0		;RESET UPDATE FLAG
	PUSH	HL		;SAVE UPDATE FLAG POINTER

;	PUSH	HL
;	LD	HL,(SPSAV3)
;	CALL	PNEXT
;	DB	' In WRSEC SPSAV3=',EOT
;	CALL	PUT4HEX
;	POP	HL

WR45:	LD	A,C	;my set sector
	LD	(SECTOR),A
	CALL	RCLDT		;PASS DRIVE AND TRACK TO BIOS
	OR	A	;because of RCLDT return
	JR	NZ,NXT341	;to set error flag
	POP	HL		;GET UPDATE FLAG POINTER
	PUSH	HL		;AND SAVE AGAIN
	INC	HL		;POINT TO DATA
;Our write requires DMA addr in HL
;
;	CALL	PNEXT
;	DB	'Writing..',EOT
;
	CALL	WRITE		;WRITE THE DATA OUT
;We must treat write errors here so that we can handle ignore correctly
	OR	A
	JR	Z,ERT1	;Skip if no errors
;	LD	(ERRFLAG),A	;For later abort
NXT341:	CALL	REPORT
;	POP	HL		;reset update flag
;	PUSH	HL
;	LD	(HL),0		;for abort
	LD	A,0		;for ignore
	JR	C,NXT349	;Ignore error
	JR	NZ,WR45
	JR	NXT349	;For abort read 'ignore'
;	LD	A,(ERRFLAG)
;	AND	40H	;The write protect status
;	JP	NZ,EXIT ;Exit if write protected, closing will do no good
;	LD	HL,0
;	PUSH	HL	;Fake a return address to zero after close
;	JP	CLOSE		;Warn of FATAL error status (update flg set)
;
ERT1:	LD	HL,WRTERRF	;OR THIS WRITE ERROR FLAG IN WITH
	OR	(HL)		;THE REST FROM THIS WRDDB
	LD	(HL),A
NXT349:	POP	HL		;GET CALLERS REG HL BACK
	LD	(HL),A	;Put the BIOS code as update flag

;	PUSH	AF
;	CALL	PNEXT
;	DB	'Write OK..',EOT
;	POP	AF

	OR	A
	RET
;
; RECALL DISK AND TRACK INFO FROM A DDB, PASS IT TO THE BIOS,
; AND SET FLAG SHOWING THAT IS HAS BEEN SENT.
; INTERNALLY REFERENCED ROUTINE.
;
RCLDT:	
	LD	HL,SENTFLG	;HAS THIS INFO ALREADY BEEN SENT?
	LD	A,(HL)
	OR	A
	LD	A,0	;*** This used to give an error return
	RET	NZ		;YES - SKIP SENDING AGAIN
	LD	(HL),SENTTOK	;NO - SET FLAG SHOWING IT HAS BEEN SENT
	LD	HL,(TRKPTR)	;GET POINTER TO DRIVE & TRACK INFO
;AND FALL THRU TO SEND IT TO BIOS
;
; SEND DRIVE AND TRACK INFO FROM A DDB TO THE BIOS.
; INTERNALLY REFERENCED ROUTINE.
;
SENDDT:	
	PUSH	HL		;SAVE TRACK POINTER
	INC	HL		;POINT TO DRIVE
	LD	C,(HL)		;GET DRIVE TO REG C
	CALL	SELECT		;PASS DISK
	POP	HL		;GET TRACK POINTER
	RET	NZ
	LD	C,(HL)		;GET TRACK TO REG C
	PUSH	HL
	CALL	SEEK		;PASS TRACK AND RETURN FROM THERE
	POP	HL
	RET
;
; MOVE A SECTOR FROM ONE PLACE IN MEMORY TO ANOTHER.
;XFER:	
;	LD	B,SECLEN
;XF1:	CALL	BANK2
;	LD	A,(DE)
;	LD	C,A		;Save it while
;	CALL	BANK0
;	LD	(HL),C	;Could do an error check here
;	INC	HL
;	INC	DE
;	DJNZ	XF1
;	CALL	BANK2	;So good/bad flag is correct
;	RET
;
RETURN:
	LD	E,A	;no errors please

;	CALL	PNEXT
;	DB	' Still in BANK2..TOS=',EOT
;	EX	(SP),HL
;	LD	C,L
;	LD	B,H
;	EX	(SP),HL
;	LD	HL,0	
;	ADD	HL,BC
;	CALL	PUT4HEX
;	CALL	PNEXT
;	DB	' Stack ptr=',EOT
;	LD	HL,0
;	ADD	HL,SP
;	CALL	PUT4HEX

	IN	A,(BITDAT)
	SET	4,A
	OUT	(BITDAT),A
	LD	A,E
	LD	SP,(SPSAV3)

;	CALL	PNEXT
;	DB	' Back in BANK0..TOS=',EOT
;	EX	(SP),HL
;	LD	C,L
;	LD	B,H
;	EX	(SP),HL
;	LD	HL,0	
;	ADD	HL,BC
;	CALL	PUT4HEX
;	CALL	PNEXT
;	DB	' Stack ptr=',EOT
;	LD	HL,0
;	ADD	HL,SP
;	CALL	PUT4HEX
;
;	DI
;	HALT	;********************

	RET

;BANK2:
;	IF	NOT NO.CACHE
;	IN	A,(BITDAT)
;	RES	4,A
;	OUT	(BITDAT),A
;	ENDIF
;	RET
;
;BANK0:
;	IF	NOT NO.CACHE
;	IN	A,(BITDAT)	;Exit, after reselecting main memory
;	SET	4,A
;	OUT	(BITDAT),A
;	ENDIF
;	RET
;
EXIT:
;	CALL	BANK0
	IN	A,(BITDAT)
	SET	4,A
	OUT	(BITDAT),A
	LD	SP,(SPSAV3)
	JP	0
;
;  \\\\\\\\\\\\\\\\                ////////////////
;   >>>>>>>>>>>>>>>>    R A M     <<<<<<<<<<<<<<<<
;  ////////////////   A R E A S    \\\\\\\\\\\\\\\\
;
; DDB ADDRESS BUFFERS.  EACH BUFFER IS FOUR W-O-R-D-S LONG,
; ONE WORD FOR THE ADDRESS OF THE BUFFER FOR EACH OF FOUR
; POSSIBLE DRIVES.  IF THERE IS NO DDB FOR A GIVEN DRIVE,
; THE DDB ADDRESS IS ZERO.
;
	ENTRY	FSTBUF	;to ease debugging make table EXTernal
FSTBUF:
	IF	NO.CACHE
RDBUF	DEFW	0,READ.DDB
WRBUF	DEFW	0,0
DIRBUF	DEFW	0,DIR.DDB

	ELSE	;For the CACHE buffer we need 1A9DH bytes for each DDB
; Allocate DDBs at 100H,1C00,3700,5200,6D00,8800-A300 leaving 1D00H bytes spare
RDBUF	DW	100H,1C00H
WRBUF	DW	3700H,5200H
DIRBUF	DW	6D00H,8800H
	ENDIF	;NO.CACHE
;
DIRADR	DW	0		;ADDRESS OF CURRENT DIRECTORY DDB
RDADR	DW	0		;ADDRESS OF CURRENT READ TRACK DDB
WRADR	DW	0		;ADDRESS OF CURRENT WRITE TRACK DDB
;
TRKPTR	DW	0		;POINTER TO TRACK & DRIVE INFO IN DDB
SENTFLG DB	0		;ZERO IF TRACK & DRIVE HAVEN'T BEEN SENT
WRTERRF DB	0		;WRITE DDB ERROR FLAG, 0 IF NO ERROR
;
REQDSK	DB	0FFH
REQTRK	DB	0FFH
REQSEC	DB	0FFH
REQDMA	DW	0FFFFH
;
ONLINE	DB	0, 0	;DRIVE ON LINE VECTOR
;
ERRFLAG: DB	0	;Used to indicate write protect error
	GLOBAL	SP3
SP3:
SPSAV3:	DW	0	;Old stack pointer addr
;
;\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
; \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
; Single Density disk driver for InfoSoft on the BigBoard
; (C) Copyright Trevor Marshall, Wait-Aid Ltd, 1981
;////////////////////////////////////////////////////////
;//////////////////////////////////////////////////////
;
;EQUATES FOR DISK CONTROLLER PORTS AND COMMAND CODES
;
STSREG	EQU	WD1771+0 ;STATUS REGISTER
CMDREG	EQU	WD1771+0 ;COMMAND REGISTER
TRKREG	EQU	WD1771+1 ;TRACK REGISTER
SECREG	EQU	WD1771+2 ;SECTOR REGISTER
DATREG	EQU	WD1771+3 ;DATA REGISTER
;
RDCMD	EQU	88H ;READ COMMAND
WRTCMD	EQU	0A8H ;WRITE COMMAND
SKCMD	EQU	1CH ;SEEK COMMAND With verify
FINCMD	EQU	0D0H ;FORCE INTR COMMAND
RSTCMD	EQU	0CH ;RESTORE COMMAND
HLOAD	EQU	04H ;RD/WRT HEAD LOAD ENABLE
;
RET	EQU	0C9H	;SUBROUTINE RETURN INSTR OPCODE
NMIVEC	EQU	0066H	;THE NON-MASKABLE INTERRUPT IS
		;USED FOR DATA SYNCRONIZATION BETWEEN
		;THE Z-80 AND 1771 DISK CONTROLLER
;
;B is unit#, CD is trk#, A is surface#, E is sector #
MYSET:
	LD	A,D
	LD	(REQTRK),A ;set TRK#
;Trk 0 test will not work here as the READ following will give 10 errors
	LD	A,E	;get sector #
	LD	(REQSEC),A
	LD	C,B	;assume C is zero (  < 255 trks)
	JP	FSETDSK
;
;
;B is unit#, HL is buffer
MYSPEC:	LD	A,C
	LD	C,B
	CP	3
	JP	Z,UEJECT	;Let GENDVR do it
	CP	0
	JR	Z,DOHOME
	CP	10		;Log in disk
	JP	Z,LOGIN
	XOR	A	;Discard all others
	RET
;
DOHOME:	CALL	SELECT	;select unit
	RET	NZ	;exit on errors
	JP	HOME	;then home drive
;
;HL is DMA ptr, A is command
MYIO:	
	LD	E,A	;DE not used for variables
	IN	A,(BITDAT)
	RES	4,A
	OUT	(BITDAT),A
	LD	(SPSAV3),SP
	LD	SP,0BF00H	;in baNk2
	LD	A,E
	OR	A	;IS A 0?
	LD	(REQDMA),HL	;for FAST buffers
	JP	Z,FREAD
	JP	FWRITE
;
S.D.SDT: DB	8	;Cluster size
	DW	S.D.DUB
	DW	52	;Directory start
	DB	64,2	;Dir entries,clusters
	DB	0	;standard directory
;
D.D.SDT: DB	32	;Cluster size (4K)
	DW	D.D.DUB
	DW	104	;Directory start (DOS trk '2')
	DB	192,2	;Dir entries,2 x 4K clusters
	DB	0	;standard directory
;
D.S.SDT: DB	16	;2k clusters
	DW	D.S.DUB
	DW	52	;start of DOS trk 2
	DB	128,2	;2 2k clusters for DIR
	DB	0	;standard directory
;
D.S.DUB: DW	77*26*2	;total blocks
	DB	0	;3rd size byte
	DW	77*26*2	;blocks/surface
	DB	26	; "	/ trk
	DB	1	;128s/blk
	DW	-1	;IBM interleaved
	DW	MYSET,MYIO,MYSPEC
;
MYDUB:
S.D.DUB: DW	77*26	;Total Blocks
	DB	0	;3rd size byte
	DW	77*26	;Blocks/surface
	DB	26	;  "   /trk
	DB	1	;128s/blk
	DW	-1	;IBM interleaving
	DW	MYSET,MYIO,MYSPEC

D.D.DUB: DW	77*52*2 ;Total Blocks
	DB	0	;3rd size byte
	DW	77*52*2 ;Blocks/surface
	DB	52	;  "   /trk
	DB	1	;128s/blk
	DW	DDINT	;interleaving
	DW	MYSET,MYIO,MYSPEC

;Use a 4 interleave (may be too fast)
DDINT:	DB	1,5,9,13,17,21,25,29,33,37,41,45,49,2,6,10
	DB	14,18,22,26,30,34,38,42,46,50,3,7,11,15,19
	DB	23,27,31,35,39,43,47,51,4,8,12,16,20,24,28
	DB	32,36,40,44,48,52

;
;Now log on the disk to see if it is double or single density
	GLOBAL	LOGIN
LOGIN:
;
;	CALL	PNEXT
;	DB	'LOGGING,',EOT
;
	PUSH	BC
	CALL	DOCLOSE

;	PUSH	AF
;	CALL	PNEXT
;	DB	'Returned from CLOSE..',EOT
;	POP	AF

	POP	BC
	RET	NZ
;
	CALL	SELECT
	RET	NZ	;to error
;
	CALL	TURNON	;After selecting drive
	BIT	7,A	 ;TEST DRIVE NOT READY BIT
	JP	NZ,ERRMSG1	; AND CONTINUE IF ITS READY
;
	LD	HL,SECTOR
	LD	(HL),1	;select sector #1
;	
	CALL	HOME
	RET	NZ
;

	LD	HL,RECLEN	;Indicate LOGIN to READ
	SET	7,(HL)
;
	LD	HL,LOGBUF	;Set DMA
;
	CALL	READ
	RET	NZ
; could Continue on here, as a failed sector will default to S/D
;First default the CREATE.DDB routine to S/D
	LD	A,27	;S/D sectors/trk+1
	LD	(POKE7+1),A
;
	LD	HL,LOGBUF ;look for 'DS' on the label
	LD	A,'D'
	CP	(HL)
	JR	NZ,SINGLD
	INC	HL
	LD	A,'S'
	CP	(HL)	;if it is then D/S
	JR	NZ,SINGLD
; Also check that it is a D/D disk
	INC	HL
	LD	A,'D'
	CP	(HL)
	JR	NZ,DOUBLES ;Failure here is D/S, S/D
	INC	HL
	CP	(HL)
	JR	NZ,DOUBLES
;It must be double sided double density, now calculate the record size
	LD	A,53	;Number of sectors/trk+1
	LD	(POKE7+1),A ;into CREATE.DDB routine
; Load the sector length/2 for read/writes
	LD	A,11B	; D/D & D/S Flags ON
	JR	SD2	;to store & RET
DOUBLES: LD	A,10B	; Bit 1 = D/S
	JR	SD2
SINGLD: LD	A,00B ; Bits 0, 1 reset
SD2:	LD	(RECLEN),A
;TRACK has already been fixed by HOME
;Now change the SDT and DUB values
	LD	HL,(CSDTE)
;	LD	A,(RECLEN)
	BIT	0,A
	RES	6,(HL)	;default S/D
	JR	Z,NOSET	;if Z it is S/D, otherwise
	SET	6,(HL)	;set flags bit (update it to D/D)
NOSET:	LD	DE,4
	ADD	HL,DE	;now pointing at clustersize entry
	EX	DE,HL	;DE is ptr
	LD	B,8	;SDT entries to update
;Include the Big Directory flag
;	LD	A,(RECLEN)
	BIT	1,A	;test D/S flag
	LD	HL,S.D.SDT	;point at SDT
	JR	Z,SETSDT	;set it
	BIT	0,A	;test D/D flag
	LD	HL,D.S.SDT	;D/S S/D
	JR	Z,SETSDT
	LD	HL,D.D.SDT	;else is D/D
SETSDT: LD	A,(HL)
	LD	(DE),A
	INC	HL
	INC	DE
	DJNZ	SETSDT
SKIPSET: 
;Initialize 2 DDBs for Directory and Read, disk A **** Ultimately all DDBs
	LD	HL,ONLINE	;Reset the online tokens
	LD	A,(UNIT)	;GET REQUESTED DRIVE
;
	LD	C,A
	LD	B,0
	ADD	HL,BC	;Pointing at on line token
	LD	(HL),0	; offline
;
	CALL	MAKE.DDB
	JP	FSETDSK
;
;A Subroutine used by INIT and SELECT to make all the DDBs
MAKE.DDB:
	LD	HL,DIRBUF	;both A & C are unit
	CALL	INDEXA		;get the DIRBUF table entry

	IF	NO.CACHE
	LD	A,L
	OR	H
	JR	Z,VB1
	ENDIF

	LD	B,2		;Directory trk#
	CALL	CREATE.DDB
VB1:	LD	HL,RDBUF	
	CALL	INDEXA

	IF	NO.CACHE
	LD	A,L
	OR	H
	JR	Z,VB2
	ENDIF

	LD	B,0FFH	;empty track, will not match any requested
	CALL	CREATE.DDB
VB2:	LD	HL,WRBUF
	CALL	INDEXA

	IF	NO.CACHE
	LD	A,L
	OR	H
	JR	Z,VB3
	ENDIF

	LD	B,0FFH
	CALL	CREATE.DDB
VB3:	RET
;
	
;Enter with unit in C
SELECT:	
;
;	CALL	PNEXT	
;	DB	'Selecting,',EOT
;
	LD	HL,UNIT	;POINT HL TO DRIVE SELECT DATA
	LD	A,(HL)	;LOAD A WITH OLD UNIT#
	LD	(HL),C	; AND STORE NEW UNIT# FROM C
;Check if drive is already logged
	LD	(OLDSCT),A	;save it in RAM
	SUB	C
	RET	Z	;If so, exit
;
	IN	A,(BITDAT)
	AND	0D8H	;MERGE IN NEW DRIVE UNIT# IN C and side 0
;Merge side 0 as disk jacket might have wrong index hole punched
	OR	C	; IN PLACE OF THE CURRENT ONE
	OUT	(BITDAT),A ; TO SELECT THE NEW DISK DRIVE
	CALL	TURNON	;After selecting drive
	AND	81H	 ;TEST DRIVE NOT READY BIT
	JR	Z,SEL2	; AND CONTINUE IF ITS READY
;	LD	B,81H	;Not ready
ERRMSG1: LD	C,A
;	LD	A,0FEH	;status
	RET
;
SEL2:	LD	A,(OLDSCT)
;We will store the old track number and RECLEN for later
	INC	HL	;POINT TO HEAD POSITION TABLE
	ADD	L	; AND ADD IN OLD UNIT# AS INDEX
	LD	L,A
	LD	A,(TRACK) ;Get current track position
;
	LD	(HL),A	; AND STORE IN TABLE @ HL
	LD	A,TTLENG ;RECTAB next
	ADD	L
	LD	L,A
	LD	A,(RECLEN)
	LD	(HL),A	;Store it in reclen table
;
SEL3:	LD	HL,RECTAB
	LD	A,L
	ADD	C	;INDEX INTO TABLE TO GET
	LD	L,A	; RECLEN OF NEW DRIVE
	LD	A,(HL)
	LD	(RECLEN),A
	LD	A,-TTLENG ;Now get track data
	ADD	L
	LD	L,A
	LD	A,(HL)	;indexed trk #
;
;Doing LOGINs here fouls up SDT table settings & LABELS
NXT111: LD	(TRACK),A ;store the current head position
NXT112:	
;Now delay 33 to 38.5 milliseconds to allow head to settle on new drive
	LD	B,7	;loop count
LP124:	IN	A,CTC3	;Get current CTC count value
	LD	C,A	;save it
LP123:	IN	A,CTC3	;loop until it has changed
	CP	C
	JR	Z,LP123	;(it may be a 'short' cycle), then
	DJNZ	LP124	;get a complete count cycle (5.5 msec)
	XOR	A	;No errors
	RET
;
;
;
HOME:	
;
;	CALL	PNEXT
;	DB	'Homing,',EOT
;
	CALL	TURNON	;CLEAR DISK CONTROLLER
	AND	81H ;Mask statuses
;	LD	B,81H	;error #
	JR	NZ,ERRMSG2 ;on error
;	XOR	A	;Already 0
	LD	(TRACK),A ;SET TRACK# IN MEM TO ZERO
	IN	A,(BITDAT) ;Now set up side 0, single D
	SET	3,A	;S/D
	RES	5,A	;Side 0
	OUT	(BITDAT),A
RESTOR:	LD	A,RSTCMD ;LOAD B WITH A RESTORE COMMAND
	CALL	CMDOUT
	AND	98H	;Mask off TRK 0 status
;	LD	B,82H	;error #
	RET	Z
ERRMSG2: LD	C,A
;	LD	A,0FEH
	RET
;
;
;
SEEK:	
;
;	CALL	PNEXT
;	DB	'Seeking,',EOT
;
	CALL	TURNON	;CLEAR DISK CONTROLLER
	AND	A,81H
	JR	NZ,ERRMSG2	;EXIT IF DRIVE NOT READY no msg
	LD	A,(TRACK) ;Get current track#
	OUT	TRKREG,A ;to the 1771
	LD	HL,RECLEN ;Dont do next if D/S
	BIT	1,(HL)	;is it D/S ?
	SCF		;clr Carry
	CCF		;ready to select side 0
	JR	Z,SK1	;skip track/side calculation if S/D
	RR	C	;now carry is side#, track is in C
;PA5 is side select
SK1:	IN	A,(BITDAT)	;get side select mask
	RES	5,A	;select side 0 again
	LD	B,0	;use B as carry mask
	RR	B	;carry into b7 of B
	RR	B
	RR	B	;now in b5 of B
 	OR	A,B	;OR BITDAT with B, the side select mask
	LD	B,A	;save BITDAT (A) to B
;Are we reading a single or double density track?
	SET	3,B	;set up BITDAT mask for S/D
	BIT	5,B	;test side bit for side 1
	JR	NZ,SK2	;so that trk 0, side 0 is always S/D
	LD	A,C	;get new trk #
	OR	A	;On side 0, is the trk # also = 0 ?
	JR	Z,NSKP	;yes, always make trk 0, side 0, S/D
;Here is either side 1 or not trk #0
SK2:	BIT	0,(HL)	;(RECLEN) (Z=S/D)
;	SET	3,B	;DD* bit is already reset ready for S/D
	JR	Z,NSKP	; exit calculation if single density disk
	RES	3,B	;here it must be DD
NSKP:	LD	A,B	;(get BITDAT mask back to A ready for)
	OUT	(BITDAT),A
;
	LD	A,(TRACK) ;GET old TRACK#
	SUB	C	;Is new trk the same as the last?
	RET	Z	;yes, exit with 0 in A
;
	LD	A,C	;get new trk#
	LD	(TRACK),A ; STORE TRACK# FOR SEEK
	OUT	(DATREG),A ;OUTPUT TRACK # TO 1771
;
	LD	A,SKCMD	;LOAD A WITH A SEEK COMMAND AND
	CALL	CMDOUT
	AND	10011000B ;MASK TO READY,SEEK AND CRC ERROR
;Now delay 16.5 to 22 milliseconds to allow head to settle
	PUSH	AF	;TANDON TM848 specified at 15 msec settling time
	LD	B,4	;loop count
LP104:	IN	A,CTC3	;Get current CTC count value
	LD	C,A	;save it
LP103:	IN	A,CTC3	;loop until it has changed
	CP	C
	JR	Z,LP103	;(it may be a 'short' cycle), then
	DJNZ	LP104	;get a complete count cycle (5.5 msec)
	POP	AF
;	LD	B,83H	;Error #
	JR	NZ,ERRMSG2
	RET		;RETURN FINAL SEEK STATUS IN A
;
;WRTERR: CALL	PNEXT
;	DB	CR,LF,'*** Disk is Write Protected',EOT
;	CALL	REPORT3
;	JP	C,RETURN.OK	;ignore
;	JR	NZ,WRITE	;retry
;	LD	A,40H	;The write protect status
;	JP	RETURN	;otherwise return to WARN and finish close
;
;We wont check if drive ready here, error handler will fix any exceptions
;Famous last words, system lost speed if following call to TURNON was omitted==
WRITE:
	PUSH	HL
	CALL	TURNON
	POP	HL
;	BIT	6,A	;look only for write protect
;	LD	B,89H	;Drive is write protected
;	JR	NZ,WRTERR	;to prompt for fix
	AND	81H	;Ready?
	JP	NZ,ERRMSG2	;to DSKERR
;
	LD	B,WRTCMD
	JR	RDWRT

READ:	
;
;	CALL	PNEXT
;	DB	'Reading,',EOT
;
	PUSH	HL
	CALL	TURNON	;try to omit this BUT reads too slow
	AND	81H
	POP	HL
;	LD	B,81H	;error #
	JP	NZ,ERRMSG2	;EXIT IF DRIVE NOT READY
;
	LD	B,RDCMD
RDWRT:	LD	(IOPTR),HL	;set DMA
	LD	HL,CMDTYP
	LD	(HL),B	;SAVE READ/WRITE COMMAND BYTE
	INC	HL
	LD	(HL),4	;SET DISK OPERATION RE-TRY COUNT
RW1:	CALL	SETUPNMI
;Allow for 128 byte sectors only at present
	LD	B,80H	;128 bytes/sector***************;;
	LD	C,DATREG ;C=1771 DATA REGISTER PORT#
	LD	A,(SECTOR) ;GET SECTOR NUMBER
	OUT	(SECREG),A ;OUTPUT SECTOR# TO 1771
	CALL	FORCE	 ;ISSUE A FORCE INTERRUPT COMMAND
	BIT	5,A	 ; TO TEST CURRENT HEAD LOAD STATUS
	LD	A,(CMDTYP) ;GET READ OR WRITE COMMAND BYTE
	JR	NZ,RW2	;JUMP IF HEAD IS ALREADY LOADED
	OR	HLOAD	; ELSE MERGE IN HLD BIT
RW2:
	LD	HL,RECLEN	;See if doing a login (short read) only
	BIT	7,(HL)

	LD	HL,(IOPTR) ;Set up DMA pointer
	OUT	CMDREG,A ;START the 1771 reading/writing

	JR	NZ,LOGLOOP	;Bit 7 was HI so short read

	BIT	5,A	;TEST IF COMMAND IS A READ OR WRITE
	JR	NZ,WLOOP ; AND JUMP TO THE CORRECT LOOP
RLOOP:	HALT
	INI		
	JP	NZ,RLOOP
RW6:	CALL	IRQ2
	AND	9CH	;MASK OFF TO READY, NOT FOUND, CRC
	JR	RW3	; AND LOST DATA STATUS BITS
;
LOGLOOP: LD	B,7AH	;Bytes to discard
LPZY2:	HALT	
	IN	A,(C)	
	DJNZ	LPZY2	;speed not important as S/D only
	LD	B,6	;Bytes to read
LPZY3:	HALT	
	INI	
	JP	NZ,LPZY3
	JR	RW6	;For error processing
;
WLOOP:	HALT
	OUTI
	JP	NZ,WLOOP
	CALL	IRQ2
	AND	0FCH	;MASK OFF AS ABOVE PLUS WRITE FAULT
RW3:	
	JR	NZ,RW7
	RETN	;not conditional
RW7:	
	LD	C,A	;Save error status in C
	LD	HL,RETRY
	DEC	(HL)	;DECREMENT RE-TRY COUNT AND
	JR	NZ,RW1	;Execute command again if not=0
;	LD	B,86H	;error # read after write
	LD	A,C
	OR	A	;Ensure error flag is set
;	LD	A,0FEH
	RET		;ELSE RETURN 1771 ERROR STATUS

;
;
;Forcing a D1 interrupt here hangs (197X app p3) 
CMDOUT: OUT	CMDREG,A ;Output the command
	CALL	SETUPNMI
IRQ2:	HALT
	CALL	RESNMI	;Restore NMI
	IN	A,STSREG
	RETN
;
FORCE:	LD	A,FINCMD
	OUT	CMDREG,A
LP7:	IN	A,STSREG
	BIT	0,A	;Still busy?
	JR	NZ,LP7
	RET
;
SETUPNMI: DI
	LD	HL,NMIVEC
	LD	D,(HL)
	LD	A,0C9H
	LD	(HL),A
	RET
;
RESNMI: LD	HL,NMIVEC	;Restore NMI contents
	LD	(HL),D
	EI
	RET
;
TURNON:	LD	A,60	;30 secs
	LD	(MOTOR),A ;RE-LOAD THE MOTOR TURN-OFF TIMER
; Test to see if new drive is ready here
	IN	A,(BITDAT)
	AND	0FBH	;ELSE RE-ENABLE THE DRIVE SELECTS
	OUT	(BITDAT),A ; AND ACTIVATE THE MOTOR RELAY
	CALL	FORCE
	BIT	7,A	;Ready?
	RET	Z	; AND EXIT IF STILL TURNED ON
	PUSH	BC
	LD	B,0 ;SET READY LOOP MAX TIMEOUT
;
TURN2:	CALL	WAIT	;WAIT 1/93 SECOND AND TEST READY
	RLA	;Test the ready status bit
	JR	NC,TURN3 ;EXIT LOOP IF DRIVE READY
	DJNZ	TURN2	; ELSE TRY AGAIN UP TO 256 TIMES
TURN3:	LD	B,20
TURN4:	CALL	WAIT	;GIVE MORE DELAY
	DJNZ	TURN4	;to ensure no CRC errors
;	CALL	FORCE	;Done in WAIT
;	AND	11011001B ;do in main routines
	POP	BC
	RET
;
;
WAIT:	IN	A,(CTC3) ;GET CURRENT CTC3 COUNT VALUE
	LD	C,A
WAIT2:	IN	A,(CTC3)
	CP	C	;TEST IF CTC3 HAS CHANGED BY 1 COUNT
	JR	Z,WAIT2	; AND LOOP UNTIL IT CHANGES
	JR	FORCE	; THEN GO TEST DRIVE READY STATUS
;
;
;****************************************
;*					*
;* DISK I/O BUFFER, PARAMETER TABLES	*
;*					*
;****************************************
;
;
	ENTRY	LOGBUF
LOGBUF:	DB	0,0,0,0,0,0
	DATA
	ENTRY	BGFIFO
BGFIFO:
BIGFIFO: DS	40H
;----------------------------------------------End of code section
	ORG	0FF80H
;
	ENTRY	VECTAB
;
MEM:	EQU	$	;Start of RAM area
;
VECTAB:	EQU	$	;INTERRUPT VECTOR TABLE STARTS HERE
SIOVEC:	EQU	$	;SPACE FOR 8 VECTORS FOR SIO
;Read these DS equivalents for LINKA as....
;		var DS  |
CTCVEC:	EQU	SIOVEC+16	;SPACE FOR 4 VECTORS FOR CTC
SYSVEC:	EQU	CTCVEC+8	;SPACE FOR 2 VECTORS FOR SYSTEM PIO
GENVEC:	EQU	SYSVEC+4	;SPACE FOR 2 VECTORS FOR GENERAL PIO
;
;
;	DISK I/O DRIVER VARIABLES
; Be careful not to straddle a Page Boundary with these
UNIT:	EQU	GENVEC+4 ;CURRENTLY SELECTED DISK#
TRKTAB:	EQU	UNIT+1	;2 DRIVE HEAD POSITION TABLE
TTLENG: EQU	2
RECTAB: EQU	TRKTAB+2	;2 Drive record length table
MOTOR:	EQU	RECTAB+2	;DRIVE MOTOR TURN-OFF TIMER
RECLEN:	EQU	MOTOR+1		;Current disk 'Record Length' and density flag
TRACK:	EQU	RECLEN+1
SECTOR:	EQU	TRACK+1
CMDTYP:	EQU	SECTOR+1	;COMMAND BYTE FOR READS/WRITES
RETRY:	EQU	CMDTYP+1	;DISK OPERATION RE-TRY COUNT
IOPTR:	EQU	RETRY+1		;DISK I/O BUFFER POINTER
OLDPTR:	EQU	IOPTR+2		;A scratch location used by RESTORE
RWFLAG:	EQU	OLDPTR+2	;Discriminates reads/writes
OLDSCT:	EQU	RWFLAG+1	;For saving the old sector # in LOGIN
;
;
;
;	KEYBOARD DATA INPUT FIFO VARIABLES
;FIFO:	EQU	OLDSCT+1	;CONSOLE INPUT FIFO
;FIFCNT: EQU	FIFO+16		;FIFO DATA COUNTER
;FIFO is now in RAM for type ahead buffer allocation
FIFCNT: EQU	OLDSCT+1
FIFIN:	EQU	FIFCNT+1	;FIFI INPUT POINTER
FIFOUT:	EQU	FIFIN+1		;FIFO OUTPUT POINTER
;
CLOSE.TICKS: EQU FIFOUT+1	;2 locs for CLOSE elapsed timer
;
;	STACK POINTER SAVE AND LOCAL STACK FOR INTERRUPT ROUTINES
;TMPSTK: EQU	FIFOUT+1+16	;Internal stack for interrupts
TMPSTK: EQU	CLOSE.TICKS+2+16
STACK	EQU	TMPSTK+16	;Stack for INIT
SPSAVE:	EQU	STACK+2		;for saving SP
SPSAV2:	EQU	SPSAVE+2	;For saving IOS SP
;
;
;	'SOFTWARE' VECTORS FOR INTERRUPT SERVICE ROUTINES
TIKVEC:	EQU	SPSAV2+2	;ONE SECOND INTERRUPT ROUTINE VECTOR
PINVEC:	EQU	TIKVEC+2	;PARALLEL CONSOLE INPUT VECTOR
SINVEC:	EQU	PINVEC+2	;SERIAL CONSOLE INPUT VECTOR
;
;
;	CLOCK-TIMER INTERRUPT VARIABLES

TIKCNT:	EQU	SINVEC+2	;BINARY CLOCK TICK COUNTER
DAY:	EQU	TIKCNT+2	;CALENDAR DAY
MONTH:	EQU	DAY+1		; MONTH
YEAR:	EQU	MONTH+1	;  YEAR
HRS:	EQU	YEAR+1	;CLOCK HOURS REGISTER
MINS:	EQU	HRS+1	;      MINUTES RETISTER
SECS:	EQU	MINS+1	;      SECONDS REGISTER
;
;	CRT OUTPUT DRIVER VARIABLES

COFLAG:	EQU	SECS+1		;used by switchstash
CURSOR:	EQU	COFLAG+1	;CURSOR POINTER
CHRSAV:	EQU	CURSOR+2	;CHARACTER OVERLAYED BY CURSOR
CSRCHR:	EQU	CHRSAV+1	;CHARACTER USED FOR A CURSOR
BASE:	EQU	CSRCHR+1	;CURRENT CONTENTS OF SCROLL REGISTER
LEADIN:	EQU	BASE+1		;STATE OF LEAD-IN SEQUENCE HANDLER
DELAY.FLG: EQU	LEADIN+1	;1 if 100Usec delay is required before display
;
;
MLNGTH	EQU	LEADIN+2-MEM	;spare loc for err in zeroing RAM
;
;-----------------------------------------------------------------

	IF	NO.CACHE

	DATA
	ENTRY	DDB	;debugging
DDB:
READ.DDB:	
	DS	1	;TRACK#
	DS	1	;DRIVE#
;Now define storage for 52 of the folowing formats
;	[DB	SECTOR
;	DB	0	;Update flag
;	DS	80H	;Data ]

	DS	52*[128+3] + 1
;include DB	0	;End marker (sector # of 0)
;
DIR.DDB: DS	1	;Track#
	DS	1	;Drive#
	DS	52*[128+3] + 1 ;NOTE: we don't need this much ********
;include DB	0	;End marker
	ENDIF	;NO.CACHE
;
	END
